應該做過android tv開發的同學都知道,在TV上使用GridView的時候,如果焦點上下移動的時候,如果移動到在螢幕上可見的第一行或者最後一行的時候,如果再繼續上下移動,
的話,是比較生硬呆滞的的上下滾動頁面,焦點移動到下一個item上,這是非常不太好的體驗效果,我們要的是比較平滑的滾動效果。
首先我們要來了解一下GridView的如下這個方法:
smoothScrollToPositionFromTop(int position,int offset,int duration);
滾動到position項目的位置,并且position項目距離GirdView上邊的距離為offset個像素,duration指定滾動需要的時間(毫秒)。
這個方法是今天的主角,雖然是主角,但不是說隻有使用這個方法就可以了?當然不是,當然也需要一定的邏輯算法配合。
今天就說說我是如何實作的。
先說說我的思路:
首先你要知道目前item所在的行,才知道按上下按鍵的時候,要滾動到哪一個postion。
public class AppsGridViewAdapter extends BaseAdapter {
private int mCurrentSelectedPosition = 0;
private int mLastSelectedPosition = 0;
private static final int ITEM_BG_COLORS[] = new int[]{
Color.parseColor("#e0c101"),
Color.parseColor("#fbaf54"),
Color.parseColor("#684cb5"),
Color.parseColor("#23a5ba"),
Color.parseColor("#03ba9f"),
Color.parseColor("#88c04c"),
Color.parseColor("#684cb5"),
Color.parseColor("#23a5ba"),
Color.parseColor("#03ba9f"),
Color.parseColor("#88c04c"),
Color.parseColor("#e0c101"),
Color.parseColor("#fbaf54"),
};
private int mColorIndex = 0;
private List<AppInfo> mApps = new ArrayList<AppInfo>();
private Context mContext;
private int mGridViewRows = 0;
public AppsGridViewAdapter(Context context, List<AppInfo> apps) {
this.mContext = context;
if(apps!=null)
this.mApps = apps;
setRows();
mColorIndex = 0;
}
private void setRows() {
int remainder = mApps.size() % 6;
mGridViewRows =(remainder == 0)? (this.mApps.size() > 0 ? this.mApps.size() / 6:0) : (mApps.size() / 6) +1;
}
public int getNumRows() {
return mGridViewRows;
}
public int getNumColumns(){
return 6;
}
@Override
public void notifyDataSetChanged() {
mColorIndex = 0;
super.notifyDataSetChanged();
}
public void setApps(List<AppInfo> apps){
mApps.clear();
if(apps!=null)
mApps = apps;
setRows();
}
public void addApp(AppInfo app){
if(app!=null && app.isValidate())
mApps.add(app);
setRows();
}
public void removeApp(AppInfo app){
if(app!=null)
mApps.remove(app);
setRows();
}
public int getCurrentSelectedPosition() {
return mCurrentSelectedPosition;
}
public int getLastSelectedPosition() {
return mLastSelectedPosition;
}
@Override
public int getCount() {
return mApps.size();
}
@Override
public Object getItem(int position) {
return mApps.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = View.inflate(mContext, R.layout.apps_page_gridview_item_layout, null);
holder = new ViewHolder();
holder.mAppIcon = (ImageView) convertView.findViewById(R.id.app_icon);
holder.mAppName = (TextView) convertView.findViewById(R.id.app_name);
holder.mAppIconColorBg = (RelativeLayout) convertView.findViewById(R.id.app_icon_color_bg);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
holder.mAppName.setText(mApps.get(position).getName());
holder.mAppIcon.setBackgroundDrawable(mApps.get(position).getIcon());
holder.mAppIconColorBg.setBackgroundColor(ITEM_BG_COLORS[mColorIndex]);
++mColorIndex;
mColorIndex =( (mColorIndex / 6) != 0 && (mColorIndex / 6) % 2 == 0 ) ? 0 :mColorIndex;
return convertView;
}
private static final class ViewHolder{
private ImageView mAppIcon;
private TextView mAppName;
private RelativeLayout mAppIconColorBg;
}
}
我們看到,Adapter中定一個了兩個方法,可以用來擷取多少行,和多少列(列一般是已知的)。
setRows() ;是每次設定給adapter資料的時候,都重新弄設定一下總行數。
getNumRows,傳回擷取目前所有的行數,getNumColumns傳回所有的列數。
OK,接下來,我們就可以通過postion來算出目前選中的postion在哪一行了。
package net.sunniwell.projector.luncher.page;
import java.util.List;
import net.sunniwell.projector.luncher.CategoryPageActivity;
import net.sunniwell.projector.luncher.R;
import net.sunniwell.projector.luncher.adapter.AppsGridViewAdapter;
import net.sunniwell.projector.luncher.bean.AppInfo;
import net.sunniwell.projector.luncher.engine.PackageAsyncTask;
import net.sunniwell.projector.luncher.engine.PackageAsyncTask.OnQueryPackageStateListener;
import net.sunniwell.projector.luncher.engine.PackageEngine;
import net.sunniwell.projector.luncher.engine.PackageEngine.OnPackageStateChangeListener;
import net.sunniwell.projector.luncher.views.CommonGridView;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView;
/**
*
* @author zhanghuagang 2017.7.6
*
*/
public class AppsPage extends BasePage implements OnPackageStateChangeListener, OnItemSelectedListener,OnQueryPackageStateListener ,OnItemClickListener{
static{
sCategory = 3;
}
private int mCurrentRow = 0;
private TextView mIndexTextView;
private CommonGridView mGridView;
private AppsGridViewAdapter mAppsAdapter;
private PackageAsyncTask mAsyncTask;
private PackageEngine mPackageEngine;
public AppsPage(CategoryPageActivity activity, ViewGroup contentView,int category) {
super(activity, contentView, category);
this.mPackageEngine = PackageEngine.getDefault(activity);
this.mPackageEngine.setOnPackageStateChangeListener(this);
}
@Override
public void initView() {
mIndexTextView = (TextView) mContentView.findViewById(R.id.index_textview);
mGridView = (CommonGridView) mContentView.findViewById(R.id.apps_gridview);
mGridView.setOnItemSelectedListener(this);
mGridView.setOnItemClickListener(this);
mAppsAdapter = new AppsGridViewAdapter(mActivity,null);
mGridView.setAdapter(mAppsAdapter);
mAppsAdapter.notifyDataSetChanged();
}
@Override
public void onResume() {
mPackageEngine.register();
queryApps();
}
private void queryApps() {
if(mAsyncTask!=null)
mAsyncTask.stop();
mAsyncTask = new PackageAsyncTask(mActivity, this);
mAsyncTask.start();
}
@Override
public void onStop() {
}
@Override
public void onPause() {
mPackageEngine.unregister();
}
@Override
public void onDestroy() {
}
private int getItemIndex(int position){
int r = position % mAppsAdapter.getNumColumns() ;
if(r == 0){
if(position > 0 ){
return (position/mAppsAdapter.getNumColumns())+1;
}else{
return 1;
}
}else{
return (position/mAppsAdapter.getNumColumns())+1;
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
if(getItemIndex(position) > mCurrentRow){
mGridView.smoothScrollToPositionFromTop(position, 0,500);
}else if(getItemIndex(position) < mCurrentRow){
mGridView.smoothScrollToPositionFromTop(position-2, 0,500);
}
mGridView.onItemSelected(parent, view, position, id);
mIndexTextView.setText(getItemIndex(position)+" | "+mAppsAdapter.getNumRows());
mCurrentRow =getItemIndex(position);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
@Override
public void onFinish(List<AppInfo> apps) {
mAppsAdapter.setApps(apps);
mAppsAdapter.notifyDataSetChanged();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
AppInfo app = (AppInfo) mAppsAdapter.getItem(position);
if(app!=null){
final Intent intent = new Intent();
intent.setClassName(app.packageName, app.className);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivity.startActivity(intent);
}
}
@Override
public void onPackageAdded(AppInfo app) {
mAppsAdapter.addApp(app);
mAppsAdapter.notifyDataSetChanged();
}
@Override
public void onPackageDeleted(AppInfo app) {
mAppsAdapter.removeApp(app);
mAppsAdapter.notifyDataSetChanged();
}
}
我的getItemIndex(int postion)就是根據position擷取目前所在行的。
然後我們在onItemSelected對平滑滑動做處理。
如果下一個擷取焦點的item所在行小于目前行,說明是向上按的,如果是大于目前行,說明是向下按住的。
這裡我們特殊的處理的向下按的,position-2,這個意思就是當我向下按的時候,每次移動兩個postion的距離,并且postion和邊緣距離為0