ListView的私人订制

转载请注明出处:http://blog.csdn.net/magic_jss/article/details/52369091;
现在开发中Android RecyclerView可能用的比较多,不过ListView作为常用控件学习它的使用和扩展也是十分重要的。简单封装了一个下拉刷新和上拉加载的ListView,你是否也想有个私人订制的ListView呢?或许这篇文章能够帮到你,如有问题恳请指正!欢迎评论哦!

效果图:
这里写图片描述

由于电脑和模拟器的原因可能不太清晰及略卡顿,真机上则很清晰及流畅。

1、自定义ListView

直接上代码

/**
 * Created by magic on 2016年5月12日.带下拉刷新/上拉加载的listview
 */
public class PullDownRefurbishLoadListView extends ListView implements
		OnScrollListener, OnClickListener {

	/**
	 * head view
	 */
	private View headView;
	/**
	 * head view height
	 */
	private int headViewHeight;
	/**
	 * 是否可以下滑刷新
	 */
	private boolean isPullDownRefurbish = false;
	/**
	 * 文本状态描述
	 */
	private TextView tev_status;
	/**
	 * 进度条
	 */
	private ImageView progressBar;
	/**
	 * foot view
	 */
	private View footView;
	/**
	 * foot view height
	 */
	private int footViewHeight;
	/**
	 * 是否可以上拉加载
	 */
	private boolean isPullHighLoad = false;
	/**
	 * 底部布局
	 */
	LinearLayout layout_listviewFoot;
	/**
	 * 底部文本状态描述
	 */
	private TextView tev_status_foot;
	/**
	 * 进度条
	 */
	ImageView progressBar_foot;
	/**
	 * 按下后的初始Y位置
	 */
	private float beginY = 0;
	/**
	 * 移动的距离
	 */
	private int moveY = 0;
	/**
	 * 正常状态
	 */
	private final static int NONE = 0;
	/**
	 * 下拉/上拉状态
	 */
	private final static int PULL = 1;
	/**
	 * 释放刷新状态
	 */
	private final static int RELEASE = 2;
	/**
	 * 刷新状态
	 */
	private final static int REFURBISH = 3;
	/**
	 * 状态
	 */
	private static int STATUS;
	/**
	 * 是否允许下拉刷新
	 */
	private boolean isRefurbishAble = true;
	/**
	 * 是否允许上拉加载
	 */
	private boolean isLoadAble = true;
	/**
	 * context
	 */
	private Context context;
	/**
	 * 动画
	 */
	private RotateAnimation rotateAnimation, rotateAnimation2;
	/**
	 * 接口
	 */
	private IPullDownRefurbishLoadListView refurbishLoadListView;

	public PullDownRefurbishLoadListView(Context context) {
		super(context);
		init(context);
	}

	public PullDownRefurbishLoadListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public PullDownRefurbishLoadListView(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	/**
	 * 添加head/foot布局
	 * 
	 * @param context
	 */
	private void init(Context context) {
		headView = LayoutInflater.from(context).inflate(R.layout.listview_head,
				null);
		this.addHeaderView(headView);
		// 设置滑动监听
		this.setOnScrollListener(this);
		headView.measure(ViewGroup.LayoutParams.MATCH_PARENT,
				ViewGroup.LayoutParams.WRAP_CONTENT);
		headViewHeight = headView.getMeasuredHeight();
		// 设置headView 偏移出屏幕
		headView.setPadding(0, -headViewHeight, 0, 0);

		tev_status = (TextView) findViewById(R.id.tev_listviewHead_status);
		progressBar = (ImageView) findViewById(R.id.prb_listviewHead_refurbish);

		footView = LayoutInflater.from(context).inflate(
				R.layout.listview_footer, null);
		this.addFooterView(footView);
		footView.measure(ViewGroup.LayoutParams.MATCH_PARENT,
				ViewGroup.LayoutParams.WRAP_CONTENT);
		footViewHeight = footView.getMeasuredHeight();

		layout_listviewFoot = (LinearLayout) footView
				.findViewById(R.id.layout_listviewFoot);
		tev_status_foot = (TextView) footView
				.findViewById(R.id.tev_listviewFoot_state);
		progressBar_foot = (ImageView) findViewById(R.id.prb_listviewFoot_load);
		tev_status_foot.setOnClickListener(this);
		this.context = context;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:

			beginY = ev.getY();

			if (STATUS == REFURBISH) {
				// 不消耗事件
				return false;
			}

		case MotionEvent.ACTION_MOVE:

			this.moveY = (int) (ev.getY() - beginY);

			if (isRefurbishAble && isPullDownRefurbish && moveY >= 0) {
				if (moveY > 0) {
					if (moveY > headViewHeight) {
						STATUS = RELEASE;
						if (moveY >= (headViewHeight + dp2px(20, context))) {
							moveY = headViewHeight + dp2px(20, context);
						}
					} else if (moveY > 0 && moveY <= headViewHeight) {
						STATUS = PULL;
					} else {
						STATUS = NONE;
					}
					setRefurbishByStatus((int) moveY);
				}
			}

			if (isLoadAble && isPullHighLoad && moveY < 0) {
				tev_status_foot.setVisibility(View.GONE);
				progressBar_foot.setVisibility(View.VISIBLE);
				if (Math.abs(moveY) > footViewHeight) {
					STATUS = RELEASE;
				} else if (Math.abs(moveY) > 0
						&& Math.abs(moveY) <= footViewHeight) {
					STATUS = PULL;
				} else {
					STATUS = NONE;
				}
			}

			break;

		case MotionEvent.ACTION_UP:
			if (isRefurbishAble && isPullDownRefurbish && moveY >= 0) {
				switch (STATUS) {
				case PULL:
					moveY = 0;
					setRefurbishByStatus(-headViewHeight);
					break;
				case RELEASE:
					STATUS = REFURBISH;
					setRefurbishByStatus((int) moveY);
					if (refurbishLoadListView != null) {
						refurbishLoadListView.refurbish();
					}
					break;

				}
			}

			if (isLoadAble && isPullHighLoad && moveY < 0) {
				switch (STATUS) {
				case PULL:
					moveY = 0;
					STATUS = NONE;
					tev_status_foot.setVisibility(View.VISIBLE);
					progressBar_foot.setVisibility(View.GONE);
					break;
				case RELEASE:
					STATUS = REFURBISH;
					progressBar_foot.clearAnimation();
					if (rotateAnimation2 != null) {
						rotateAnimation2.cancel();
					}
					setAnimationToProgressBarFoot();
					break;
				}
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 设置head布局的上内边距
	 * 
	 * @param size
	 */
	private void setHeadPaddingTop(int size) {
		size = size + (-headViewHeight);
		headView.setPadding(0, size, 0, 0);
	}

	/**
	 * 根据状态设置刷新HeadView显示的内容
	 */
	private void setRefurbishByStatus(int moveY) {
		switch (STATUS) {
		case NONE:
			tev_status.setText("下拉刷新");
			progressBar.setImageResource(R.drawable.ic_ptr_pull);
			setHeadPaddingTop(-headViewHeight);
			break;
		case PULL:
			tev_status.setText("下拉刷新");
			progressBar.setImageResource(R.drawable.ic_ptr_pull);
			setHeadPaddingTop(moveY);
			break;
		case RELEASE:
			tev_status.setText("释放刷新");
			progressBar.setImageResource(R.drawable.ic_ptr_release);
			setHeadPaddingTop(moveY);
			break;
		case REFURBISH:
			tev_status.setText("刷新中");
			progressBar.setImageResource(R.drawable.ic_ptr_loading);
			setHeadPaddingTop(headViewHeight);

			if (rotateAnimation == null) {
				rotateAnimation = new RotateAnimation(0.0f, 180.0f,
						Animation.RELATIVE_TO_SELF, 0.5f,
						Animation.RELATIVE_TO_SELF, 0.5f);
				rotateAnimation.setDuration(150);
				rotateAnimation.setRepeatCount(-1);
			}
			progressBar.setAnimation(rotateAnimation);
			rotateAnimation.start();
			break;
		}
	}

	/**
	 * 设置对外公开接口
	 * 
	 * @param pullDownRefurbish
	 */
	public void setIPullDownRefurbish(
			IPullDownRefurbishLoadListView refurbishLoadListView) {
		this.refurbishLoadListView = refurbishLoadListView;
	}

	/**
	 * 刷新完成执行
	 */
	public void setPullDownRefurbishFinish() {
		moveY = 0;
		STATUS = NONE;
		setRefurbishByStatus((int) moveY);
		progressBar.clearAnimation();
		rotateAnimation.cancel();
	}

	/**
	 * 加载完成执行
	 */
	public void setPullDownLoadFinish() {
		moveY = 0;
		STATUS = NONE;
		progressBar_foot.clearAnimation();
		rotateAnimation2.cancel();
		progressBar_foot.setVisibility(View.GONE);
		tev_status_foot.setVisibility(View.VISIBLE);
	}

	/**
	 * 设置是否允许下拉刷新
	 * 
	 * @param isRefurbishAble
	 */
	public void setRefurbishAble(boolean isRefurbishAble) {
		this.isRefurbishAble = isRefurbishAble;
	}

	/**
	 * 设置是否可以上拉加载
	 * 
	 * @param isLoadAble
	 */
	public void setLoadAble(boolean isLoadAble) {
		this.isLoadAble = isLoadAble;
		if (!isLoadAble) {
			layout_listviewFoot.setVisibility(View.GONE);
		}
	}

	@Override
	public void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) {
		// 参数:
		// 查看其滚动状态的视图
		// firstvisibleitem -第一个可见的细胞指数(忽略如果visibleitemcount = = 0)
		// visibleitemcount -可见细胞数
		// totalitemcount -在列表适配器项目数

		// arg1为0时 列表在最顶部
		isPullDownRefurbish = arg1 == 0 ? true : false;
		// arg1为最后一个时arg1==arg3
		isPullHighLoad = (arg1 + arg2) == arg3 ? true : false;
	}

	@Override
	public void onScrollStateChanged(AbsListView arg0, int arg1) {
	}

	/**
	 * dp转px
	 */
	private int dp2px(float value, Context context) {
		final float scale = context.getResources().getDisplayMetrics().densityDpi;
		return (int) (value * (scale / 160) + 0.5f);
	}

	/**
	 * 为progressBar_foot设置动画
	 */
	private void setAnimationToProgressBarFoot() {
		if (rotateAnimation2 == null) {
			rotateAnimation2 = new RotateAnimation(0.0f, 180.0f,
					Animation.RELATIVE_TO_SELF, 0.5f,
					Animation.RELATIVE_TO_SELF, 0.5f);
			rotateAnimation2.setDuration(150);
			rotateAnimation2.setRepeatCount(-1);
		}
		progressBar_foot.setAnimation(rotateAnimation2);
		rotateAnimation2.start();
		if (refurbishLoadListView != null) {
			refurbishLoadListView.load();
		}
	}

	@Override
	public void onClick(View v) {
		// 点击查看更多
		tev_status_foot.setVisibility(View.GONE);
		progressBar_foot.setVisibility(View.VISIBLE);
		setAnimationToProgressBarFoot();
	}

	/**
	 * 接口
	 */
	interface IPullDownRefurbishLoadListView {
		/**
		 * 刷新事件回调
		 */
		void refurbish();

		/**
		 * 加载回调
		 */
		void load();
	}

}

以上代码主要步骤:

  • 初始化的时候添加HeadView、FootView。
  • 继承ListView实现OnScrollListener接口,重写onScroll方法,因为onScroll方法在ListView滑动的时候会一直回调,因此在onScroll方法中判断是否处于ListView的顶部/底部,从而处理下拉刷新/上拉加载的展现时机。
  • 重写onTouchEvent方法,在按下、滑动、抬起的时候动态处理HeadView、FootView的展现。
  • 添加回调接口,设置回调方法。

注释比较清楚,不在赘述。

2、xml文件

主布局

<?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" >

    <com.magic.test_listviewrefurbish.PullDownRefurbishLoadListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
</LinearLayout>

layout/listview_head.xml

<?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" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingBottom="20dp"
        android:paddingTop="20dp" >

        <ImageView
            android:id="@+id/prb_listviewHead_refurbish"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_ptr_pull" />

        <TextView
            android:id="@+id/tev_listviewHead_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="下拉刷新"
            android:textColor="#afafaf"
            android:textSize="12sp" />
    </LinearLayout>

</LinearLayout>

listview_footer.xml

<?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:descendantFocusability="blocksDescendants"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/layout_listviewFoot"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingBottom="15dp"
        android:paddingTop="15dp" >

        <TextView
            android:id="@+id/tev_listviewFoot_state"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="查看更多"
            android:textColor="@android:color/darker_gray" />

        <ImageView
            android:id="@+id/prb_listviewFoot_load"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_ptr_loading"
            android:visibility="gone" />
    </LinearLayout>

</LinearLayout>
3、Activity中使用
/**
 * Created by magic on 2016年5月12日.带下拉刷新/上拉加载的listview
 */
public class MainActivity extends Activity implements OnItemClickListener {

	PullDownRefurbishLoadListView listView;
	MyAdapter adapter;
	List<String> list = new ArrayList<String>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		initView();
	}

	private void initView() {
		listView = (PullDownRefurbishLoadListView) findViewById(R.id.listview);
		list.add("a");
		adapter = new MyAdapter(this, list);
		listView.setAdapter(adapter);
		listView.setOnItemClickListener(this);
		//设置是否可以下拉刷新,默认为true
		listView.setRefurbishAble(true);
		//设置是否可以上拉加载,默认为true
		listView.setLoadAble(true);

		listView.setIPullDownRefurbish(new IPullDownRefurbishLoadListView() {

			@Override
			public void refurbish() {
				new Handler().postDelayed(new Runnable() {

					@Override
					public void run() {
						list.add(0, "c");
						adapter.notifyDataSetChanged();
						listView.setPullDownRefurbishFinish();
					}
				}, 2000);
			}

			@Override
			public void load() {
				new Handler().postDelayed(new Runnable() {

					@Override
					public void run() {
						list.add("b");
						adapter.notifyDataSetChanged();
						listView.setPullDownLoadFinish();
					}
				}, 2000);
			}
		});
	}

	@Override
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		Toast.makeText(this, "Hello  " + arg2, Toast.LENGTH_SHORT).show();
	}
}

Adapter比较简单就不写了!

整个项目下载地址:http://download.csdn.net/detail/magic_jss/9616947;

END!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值