您的位置:首页 > 移动开发 > Android开发

android ListView-选中项固定在某一项

2014-10-31 16:34 190 查看
最近做盒子项目,需要做一个列表效果,用遥控器上下移动列表时,选中项始终在第三行,且移动时有动画。起初想到使用自定义View来实现,这样确实很方便,但是有一个问题,当我需要更新列表时需要每次重新删除所有的View,再重新创建,而且当列表项很多时,需要创建很多的View,而且动画还需要自己重写。然后就想到是否可以在ListView的基础上实现的这样的效果,因为即使列表项很多时,ListView的创建的View个数也很少,而且它的Adapter刷新机制很方便,而且可以直接使用它的动画。

效果图如下:



1.创建BaseListView,此实际上是一个RelativeLayout,在其中添加一个ListView,封装了很多ListView的方法,但是ListView不对外开放。

(1).固定Item在第三项,使用ListView的smoothScrollBy方法来控制。其中的难点是每次选中项后要计算移动的偏差值。

(2).当移动到列表的最上面或者最下面时,smoothScrollBy方法失效,选中项不会固定在第三项的位置,会直接移到上面去或者下面。解决的方法是在列表上方添加List几个头部和list几个尾部,但是头部和尾部都不能选中。

直接贴出BaseListView的代码:

package com.hm.test.view.base;

import com.hm.test.R;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;

public class BaseListView extends RelativeLayout {

	//选中项和非选中项的高度不一样。
	private int mFocusedWidth = 400;
	private int mFocusedHeight = 140;
	private int mNormalWidth = 400;
	private int mNormalHeight = 80;

	//固定显示十项
	private int mShowItemCount = 10;
	//默认选中项始终在第三项
	private int mBaseIndex = 2;

	//ListView,不对外开放
	private ListView mListView;

	private Drawable mBaseItemMask;
	private Drawable mBaseitemBorder;

	private OnItemClickListener mItemClickListener;
	private OnItemSelectedListener mItemSelectedListener;

	public BaseListView(Context context) {
		this(context, null);
	}

	public BaseListView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public BaseListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		setWillNotDraw(false);

		final Resources res = getResources();
		mFocusedWidth = res.getDimensionPixelSize(R.dimen.baselist_item_focused_width);
		mFocusedHeight = res.getDimensionPixelSize(R.dimen.baselist_item_focused_height);
		mNormalWidth = res.getDimensionPixelSize(R.dimen.baselist_item_normal_width);
		mNormalHeight = res.getDimensionPixelSize(R.dimen.baselist_item_normal_height);

		mBaseItemMask = res.getDrawable(R.drawable.base_item_bottom_mask);
		mBaseitemBorder = res.getDrawable(R.drawable.base_item_border);

		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(mNormalWidth, mNormalHeight
				* (mShowItemCount - 1) + mFocusedHeight);
		lp.addRule(RelativeLayout.CENTER_IN_PARENT);
		mListView = new MyListView(context, attrs, defStyle);
		mListView.setSelector(R.drawable.base_item_view_bg);
		mListView.setDivider(null);
		mListView.setCacheColorHint(Color.TRANSPARENT);
		mListView.setOnItemClickListener(mBaseItemClickListener);
		mListView.setOnItemSelectedListener(mBaseItemSelectedListener);

		addView(mListView, lp);
	}

	private OnItemClickListener mBaseItemClickListener = new OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			if (mItemClickListener != null) {
				mItemClickListener.onItemClick(parent, view, position - mListView.getHeaderViewsCount(), id);
			}
		}
	};

	private OnItemSelectedListener mBaseItemSelectedListener = new OnItemSelectedListener() {

		@Override
		public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
			Log.e("//////", "onItemSelected   " + position + "  " + view.getTop() + "   " + parent.getScrollY());
			mListView.scrollTo(mListView.getScrollX(), 0);
			if (view != null) {
				//此处选中时调用ListView的移动动画,计算偏移值:通过选中View的top和第三项的高度来计算
				mListView.smoothScrollBy(view.getTop() - mBaseIndex * mNormalHeight - mListView.getScrollY(), 250);
			}
			if (mItemSelectedListener != null) {
				mItemSelectedListener.onItemSelected(parent, view, position - mListView.getHeaderViewsCount(), id);
			}
		}

		@Override
		public void onNothingSelected(AdapterView<?> parent) {
			if (mItemSelectedListener != null) {
				mItemSelectedListener.onNothingSelected(parent);
			}
		}
	};

	//封装ListView的setAdapter方法
	public void setAdapter(BaseItemAdapter adapter) {
		mListView.setAdapter(adapter);
	}

	//封装ListView的getAdapter方法
	public BaseItemAdapter getAdapter() {
		ListAdapter adapter = mListView.getAdapter();
		if (adapter instanceof BaseItemAdapter) {
			return (BaseItemAdapter) adapter;
		} else if (adapter instanceof HeaderViewListAdapter) {
			HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) adapter;
			ListAdapter listAdapter = headerAdapter.getWrappedAdapter();
			if (listAdapter instanceof BaseItemAdapter) {
				return (BaseItemAdapter) listAdapter;
			}
		}
		return null;
	}

	//封装ListView的setOnItemClickListener方法
	public void setOnItemClickListener(OnItemClickListener l) {
		mItemClickListener = l;
	}

	//封装ListView的setOnItemSelectedListener方法
	public void setOnItemSelectedListener(OnItemSelectedListener l) {
		mItemSelectedListener = l;
	}

	//封装ListView的setSelection方法
	public void setSelection(int position) {
		BaseItemAdapter adapter = getAdapter();
		int realPositon = 0;
		if (adapter != null) {
			int size = adapter.getCount();
			if (position < 0) {
				realPositon = 0;
			} else if (position >= size) {
				realPositon = size - 1;
			} else {
				realPositon = position;
			}
		}
		mListView.setSelection(realPositon + mListView.getHeaderViewsCount());
	}

	//在dispatchDraw中将焦点框画出来
	@Override
	protected void dispatchDraw(Canvas canvas) {
		int width = getWidth();
		int left = 0;
		int top = mListView.getTop() + mBaseIndex * mNormalHeight;
		int right = left + width;
		int bottom = top + mFocusedHeight;
		mBaseItemMask.setBounds(left, top, right, bottom);
		mBaseItemMask.draw(canvas);

		Rect padding = new Rect();
		mBaseitemBorder.getPadding(padding);

		left = mListView.getLeft() - padding.left;
		right = left + mFocusedWidth + padding.left + padding.right;
		top = mListView.getTop() + mBaseIndex * mNormalHeight - padding.top;
		bottom = top + mFocusedHeight + padding.top + padding.bottom;
		mBaseitemBorder.setBounds(left, top, right, bottom);
		mBaseitemBorder.draw(canvas);

		super.dispatchDraw(canvas);
	}

	//自定义的ListView
	private class MyListView extends ListView {

		public MyListView(Context context, AttributeSet attrs, int defStyle) {
			super(context, attrs, defStyle);

			//关键点在这个地方:如果不加头和尾,当移动到上面或者下面时,不能固定到第三项
			for (int i = 0; i < mBaseIndex; i++) {
				View headerView = new View(context);
				AbsListView.LayoutParams headerLp = new AbsListView.LayoutParams(mNormalWidth, mNormalHeight);
				headerView.setLayoutParams(headerLp);
				//添加时必须要传递参数false,使头和尾不能选中
				addHeaderView(headerView, null, false);
			}

			for (int i = 0; i < mShowItemCount - mBaseIndex - 1; i++) {
				View footerView = new View(context);
				AbsListView.LayoutParams footerLp = new AbsListView.LayoutParams(mNormalWidth, mNormalHeight);
				footerView.setLayoutParams(footerLp);
				addFooterView(footerView, null, false);
			}

			setPadding(0, 0, 0, 0);
		}

		//此处用来防止快速移动时,造成选中项和焦点框对不齐的问题,这样做引起了一个问题,快速移动时有点慢
		@Override
		public boolean dispatchKeyEvent(KeyEvent event) {
			int action = event.getAction();
			int keyCode = event.getKeyCode();
			View view = mListView.getSelectedView();
			if (action == KeyEvent.ACTION_DOWN && view != null) {
				int top = view.getTop();
				if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP) {
					if ((mBaseIndex * mNormalHeight) != top) {
						return false;
					}
				}

			}
			return super.dispatchKeyEvent(event);
		}

	}

}


2.创建ListView的Adapter(BaseItemAdapter),使用ViewHolder机制来优化,此处就不做详细介绍了。

3.使用

(1)代码结构



(2)在xml使用BaseListView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"
        android:textColor="@android:color/white"
        android:textSize="40sp" />

    <com.hm.test.view.base.BaseListView
        android:id="@+id/test_layout_baselist"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>


(3)继承BaseItemData(TestItemData)和BaseItemView(TestItemView),然后在activity中创建BaseItemAdapter。

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_test);
		mBaseListView = (BaseListView) findViewById(R.id.test_layout_baselist);

		BaseItemAdapter adapter = new BaseItemAdapter(this);
		for (int i = 0; i < 15; i++) {
			TestItemData data = new TestItemData();
			data.index = i;
			adapter.add(data);
		}
		mBaseListView.setAdapter(adapter);

	}


4.不足之处:

(1)遥控器快速向下滑动时,移动很慢。

(2)ListView不开放,无法使用一些ListView的特性,特别是一些特殊的需求,如需添加头和尾。

代码下载链接:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: