项目中有这样一个需求,ListView列表里面的Item选中项(即焦点所在item),需要始终保持在LsitView的中间位置,即不管是按遥控器下键还是上键,焦点所在的item必须能移回到整个listView的居中位置。
如图中所示:
默认情况下,当我们按下键选择item的时候,焦点会一直往下走,当选择到当前ListView中可视的最后一个item的时候,焦点就会固定在最后一个item上,比如这时候选择了item5,那么我们在按下键选择item6的时候,焦点依旧在item5的位置,但是内容已经被item6填充了,item5则上移了一个位置。
根据我们的需求,比如我的选中焦点始终默认在item3的位置(即ListView的中间位置),也就是说当我下键选择item4的时候,焦点肯定下移了一个item的位置,这时候我就需要再把这个item4上移一个位置,即回到item3的位置。那么此时就用到了ListView的scroll方法,使用这些方法就相当于用手势控制了滚动条的位置,根据滚动的距离,我们就可以把整个listview的数据上移或下移的。
目前我这里使用到的方法是:
smoothScrollToPositionFromTop(
int position,
int offset,
int duration)
postion参数表示要选中的item在adapter中的位置;
offset表示我当前选中的pisition上的item距离Listview的顶部的位置,比如我这里把这个距离设置成listview的高度的一半,那么就可以让我选中的item始终跟ListView的顶部有一半的距离。这样就实现了居中;
duration表示平滑移动的时间,时间越久移动得越缓慢,看起来越平滑。
还有一种情况下,向下按键的时候,需要选中的item(即焦点)始终保持在ListView的可见item的第一个,向上按键的时候,需要选中的item(即焦点)始终保持在ListView可见item的最后一个上。那么这里就用上了另外一个方法:
smoothScrollBy(
int distance,
int duration)
distance参数表示移动的距离,
duration表示移动的时间
实际使用时,我们按下键的时候,只需要向上移动一个item距离,按上键的时候,值需要向下移动一个item的距离,就能实现效果了。
胡打乱撞的找了些资料看看,基本上解决了需求,但是具体这两个方法和其他相类似的方法有些什么区别和作用还需要进一步的认识。
记录已下,以后理解了好补充。
非常感谢这篇文章:android ListView TV 通过遥控器上下 smooth滑动
让我理解了不少,唯一的缺憾是不知道作者是谁,没法深入请教啊~
顺便贴已下完整的源码:
package com.golive.view;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.ListAdapter;
import android.widget.ListView;
public class SmoothScrollListView extends ListView implements OnKeyListener {
private String TAG = SmoothScrollListView.class.getSimpleName();
private int itemsCount;
private int itemHeight;
private ListAdapter adapter;
private int scrollDuration = 100;
private boolean isScrollTop;
private Timer timer;
private OnScrollBottomListener onScrollBottomListener;
private OnScrollTopListener onScrollTopListener;
public SmoothScrollListView(Context context) {
this(context, null);
}
public SmoothScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnKeyListener(this);
this.setSmoothScrollbarEnabled(true);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (adapter != null) {
// 获取每个item 的高度,因为要调用滑动的方法,每次滑动的距离就是item 的高度
itemHeight = this.getChildAt(0).getHeight();
}
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
this.adapter = adapter;
// 获取listview
// item的count,一定要是由adapter获得,不能通过listView,因为listView是动态添加删除孩子的,可以打印一下比较看看
itemsCount = adapter.getCount();
}
/**
* 设置滚动动画的滚动时间
*
* @param scrollDutation
*/
public void setScrollDuration(int scrollDutation) {
this.scrollDuration = scrollDutation;
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
return false;
}
// 获取当前被选中的位置
int currentItemPosition = this.getSelectedItemPosition();
// this.setSelectionFromTop(currentItemPosition, this.getHeight()/2);
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
// 当是倒数第二个的时候
if (currentItemPosition == itemsCount - 2) {
// 如果listView的最后一个可见Item是倒数第二个item,或者是倒数第一个item同时timer不为空,这时要滚动一次,并让最后一个item获取焦点
if (this.getLastVisiblePosition() == itemsCount - 2
|| (this.getLastVisiblePosition() == itemsCount - 1 && timer != null)) {
Log.e(TAG,"倒数第二个");
this.smoothScrollBy(itemHeight, scrollDuration);
if (timer == null) {
smoothScrollToBottom();
} else {
timer.cancel();
timer = null;
// 延迟一下,再让最后一个item编程selected状态,不让没有动画,太突兀
smoothScrollToBottom();
}
// this.smoothScrollToPositionFromTop(itemsCount - 1, 0,
// scrollDuration);
// this.setSelection(itemsCount - 1);
}
return false;
} else if (currentItemPosition == itemsCount - 1) {
setSelection(0);//这里设置循环选择,最后一个item的时候,按键向下,默认设置选中第一个item
// 当是最后一个item是selectionItem,则给出回调,让他不要在滚动了
if (onScrollBottomListener != null) {
onScrollBottomListener.onScrollBottom();
}
return true;
} else {
// 是中间其他状态的时候,滚动一个item的距离,不保证选中的item具体在ListView的什么位置。
//比如,当前选中的item0在ListView的最顶端,则选择下一个item1时,这个item1会移动到之前选中的item0的位置
//this.smoothScrollBy(itemHeight, scrollDuration);
this.smoothScrollToPositionFromTop(currentItemPosition + 1,
this.getHeight()/2, scrollDuration);//始终保持当前选择的item在ListView的最中间
return false;
}
}
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
if (currentItemPosition == 1) {
if (this.getChildAt(0).isFocusable() == false) {
this.smoothScrollBy(-itemHeight, scrollDuration);
}
return false;
} else if (currentItemPosition == 0) {
setSelection(itemsCount-1);//第一个item的时候,按键向上默认选中最后一个item
if (onScrollTopListener != null) {
onScrollTopListener.onScrollTop();
}
return isScrollTop;
} else {
//两种选择,原理和按下键一样
// this.smoothScrollBy(-itemHeight, scrollDuration);
this.smoothScrollToPositionFromTop(currentItemPosition - 1,
this.getHeight()/2, scrollDuration);
return false;
}
}
return false;
}
private void smoothScrollToBottom() {
Log.e(TAG,"平滑移动到最后");
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
SmoothScrollListView.this.post(new Runnable() {
@Override
public void run() {
SmoothScrollListView.this
.setSelection(SmoothScrollListView.this
.getLastVisiblePosition());
}
});
}
}, scrollDuration / 3);
}
/**
* 当滚动到底部的时候的监听
*/
public interface OnScrollBottomListener {
void onScrollBottom();
}
public void setOnScrollBottomListener(
OnScrollBottomListener onScrollBottomListener) {
this.onScrollBottomListener = onScrollBottomListener;
}
/**
* 当滚动到顶部的时候的监听
*/
public interface OnScrollTopListener {
void onScrollTop();
}
public void setOnScrollTopListener(OnScrollTopListener onScrollTopListener) {
isScrollTop = true;
this.onScrollTopListener = onScrollTopListener;
}
}