Android TV 自定义listview平滑滑动
2019-07-04 本文已影响0人
LoveXue
由于在TV上面的操作不是通过手指来滑动,需要用遥控器来操作,所以Listview中item的焦点处理就显得很重要,但自带的listview会有一个缺点导致操作体验不是很好,那就是item的焦点会随着上下按键显示在最上方和最下方,而且也没有动画效果。本篇文章介绍一种焦点在中间位置,listview上下滑动的实现方法,这种效果在TV上体验比较好,效果图如下:
Listview.gif
使用到的方法有:
1.重绘焦点的位置
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (this.isFocused()) {// 绘制焦点框的位置
Log.i(TAG, "onDraw = =======");
Rect rect = new Rect();
rect.set(0, itemHeight * itemFocusInCenter
+ getItemFocusCurrentPos(), itemWidth, itemHeight
* (itemFocusInCenter + 1) + getItemFocusCurrentPos());
mSelector.setBounds(rect);
mSelector.draw(canvas);
}
}
根据每次移动的距离去重新绘制焦点框的位置,当焦点框处于中间位置的时候就去移动listview,这时候用到的就是smoothScrollBy(int distance, int duration),其中distance参数表示移动的距离,duration表示移动的时间。
2.在onKeyDown按键事件里去判断并计算移动的距离
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
View view = getSelectedView();
Log.i(TAG, "onKeyDown ###############################");
int currentPositon = getSelectedItemPosition();
if (view == null)
return false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
if(itemFocusInCenter <= currentPositon){
smoothScrollBy(itemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
case KeyEvent.KEYCODE_DPAD_UP:
if(currentPositon < itemsCount - itemFocusInCenter){
smoothScrollBy(-itemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
}
return super.onKeyDown(keyCode, event);
}
以上就是实现该效果的重要代码,最重要的就是焦点位置的判断以及焦点的重绘和每次移动距离的计算,理解了这几点之后,实现起来就容易多了。
顺便把全部代码贴出来:
public class TvSmoothMoveListView extends ListView {
private String TAG = "TvSmoothMoveListView";
private int mItemsCount,mItemHeight,mItemWidth;
private int mShowItemSize = 7;//显示的item的个数
private int mItemCenterFocus;//中间位置
private int mCurrentPositon;
private int mDataSize = 0;
private ListAdapter adapter;
private int scrollDuration = 300;
private final int SHOW_CURRENT_FOCUS = 0;
private int mColorWhite = Color.parseColor("#ffffff");
private int mColorBlue = Color.parseColor("#42b3ff");
private Drawable mSelector = null;
public TvSmoothMoveListView(Context context) {
super(context);
}
public TvSmoothMoveListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TvSmoothMoveListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private void init(){
mSelector = getResources().getDrawable(R.drawable.selected2);
setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 预防切换焦点的时候出现错乱的情况
if (scrollState == SCROLL_STATE_IDLE) {
View itemView = getChildAt(0);
if (itemView != null) {
float deltaY = itemView.getY();
if (deltaY == 0) {
return;
}
if (Math.abs(deltaY) < mItemHeight / 2) {
smoothScrollBy(getDistance(deltaY), 10);
} else {
smoothScrollBy(getDistance(mItemHeight + deltaY), 10);
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});
}
private int getDistance(float scrollDistance) {
if (Math.abs(scrollDistance) <= 2) {
return (int) scrollDistance;
} else if (Math.abs(scrollDistance) < 12) {
return scrollDistance > 0 ? 2 : -2;
} else {
return (int) (scrollDistance / 6);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (adapter != null && getChildAt(0) != null) {
mItemHeight = getChildAt(0).getHeight();
mItemCenterFocus = mShowItemSize / 2;
}
}
@Override
protected void onMeasure(int arg0, int arg1) {
super.onMeasure(arg0, arg1);
mItemWidth = getWidth();
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (this.isFocused()) {// 绘制焦点框的位置
Rect rect = new Rect();
rect.set(0, mItemHeight * mItemCenterFocus
+ getItemFocusCurrentPos(), mItemWidth, mItemHeight
* (mItemCenterFocus + 1) + getItemFocusCurrentPos());
mSelector.setBounds(rect);
mSelector.draw(canvas);
}
}
/**
* 通过当前焦点位置计算出来需要移动的距离
* @return
*/
private int getItemFocusCurrentPos(){
int currentSelectPos = getSelectedItemPosition();
if(mItemCenterFocus > currentSelectPos){
return (currentSelectPos - mItemCenterFocus) * mItemHeight;
}
if(currentSelectPos > (mItemsCount - 1) - mItemCenterFocus){
return (currentSelectPos - ((mItemsCount - 1) - mItemCenterFocus)) * mItemHeight;
}
return 0;
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
this.adapter = adapter;
mItemsCount = adapter.getCount();
Log.i(TAG, "getCount ################# " + mItemsCount);
}
/**
* 移动到顶端
*/
public void startMoveTop(){
setSelectionFromTop(mItemCenterFocus, mItemHeight * mItemCenterFocus);
}
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case SHOW_CURRENT_FOCUS://防止选中的item和焦点不在一起
int currentSelectPos = getSelectedItemPosition();
setSelectionFromTop(currentSelectPos, mItemHeight * mItemCenterFocus + getItemFocusCurrentPos());
break;
default:
break;
}
};
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
View view = getSelectedView();
Log.i(TAG, "onKeyDown ###############################");
int currentPositon = getSelectedItemPosition();
if (view == null)
return false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
if(mItemCenterFocus <= currentPositon){
smoothScrollBy(mItemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
case KeyEvent.KEYCODE_DPAD_UP:
if(currentPositon < mItemsCount - mItemCenterFocus){
smoothScrollBy(-mItemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
}
return super.onKeyDown(keyCode, event);
}
}