android自定义带下拉刷新和Checkbox的ListView
下拉刷新和Checkbox的ListVi,下拉刷新和选择框的ListView2016-07-10
(尊重原创转载请说明来处,谢谢)
android自定义控件,就是根据自己的喜好、项目需求随意设计制作控件的外表和功能,不可谓不霸气!之前写了一篇关于带有编辑和删除功能的侧滑ListView,感觉效果不错的样子,所以现在再来写一篇最近实现的一个控件:带有下拉刷新和选择框的ListView,咱也是有点审美的人是不是,那还是老套路大家先看脸呗,毕竟相貌过得去才有干劲嘛~~~
个人呢觉得程序嘛,重要的是思路,只要有了思路,问题肯定是可以解决的;反过来说,如果只注重细枝末节而忽略了总体思想,效果很可能是不理想的,印象也不是最深刻的。那么问题来了,我为什么要说这些呢?因为下面我会主要讲解自己的思路,最后还会附上亲测可以运行的代码供大家学习,欢迎各位大神拍砖:
1:先说一下选择框:大家一定会认为我在item的布局中放置了一个checkbox是不是?我确实是这么做的,但是呢,checkbox的优先级是高于listview的,所以为了解决冲突,在checkbox的布局文件中添加了这些属性,
android:focusable="false" android:focusableInTouchMode="false" android:clickable="false"此外在adapter中使用了HashMap<Integer,Boolean>来记录checkbox的选中状态,点击一次选中,再次点击时取消
item代码:
<?xml version="1.0" encoding="utf-8"?>
<!-- ListView的头部 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- 内容 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/head_contentLayout" >
<!-- 箭头头像、进度条 -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="50dp"
android:layout_centerVertical="true">
<!-- 箭头 -->
<ImageView
android:id="@+id/head_arrowImageView"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:src="@drawable/upward"/>
<!-- 进度条 -->
<ProgressBar
android:id="@+id/head_progressBar"
android:layout_width="40dp"
android:layout_height="40dp"
style="?android:attr/progressBarStyleSmall"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
<!-- 提示、最近更新 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="vertical"
android:gravity="center_horizontal">
<!-- 提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:textColor="@android:color/white"
android:id="@+id/head_tipsTextView"
android:textSize="20sp"
android:text="head_tips_text"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>2:下拉刷新问题:首先呢是自定义了一个title布局文件,布局文件里有TextView、Img什么的,而这个布局文件是依靠代码将它添加到listview中,也就是说listview多了一项,只不过这个item平时是隐藏的,当手指滑动时,代码控制它动态显示或者隐藏,headTitle布局代码:
<?xml version="1.0" encoding="utf-8"?>
<!-- ListView的头部 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- 内容 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/head_contentLayout" >
<!-- 箭头头像、进度条 -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="50dp"
android:layout_centerVertical="true">
<!-- 箭头 -->
<ImageView
android:id="@+id/head_arrowImageView"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:src="@drawable/upward"/>
<!-- 进度条 -->
<ProgressBar
android:id="@+id/head_progressBar"
android:layout_width="40dp"
android:layout_height="40dp"
style="?android:attr/progressBarStyleSmall"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
<!-- 提示、最近更新 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="vertical"
android:gravity="center_horizontal">
<!-- 提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:textColor="@android:color/white"
android:id="@+id/head_tipsTextView"
android:textSize="20sp"
android:text="head_tips_text"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>package com.lei.diyrefresh;
import android.widget.ListView;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
public class RefreshCheckBoxListView extends ListView implements OnScrollListener {
private final static int RELEASE_TO_REFRESH = 0;
private final static int PULL_TO_REFRESH = 1;
//正在刷新
private final static int REFRESHING = 2;
//刷新完成
private final static int DONE = 3;
private final static int LOADING = 4;
private final static int RADIO = 3;
private LayoutInflater mInflater;
private LinearLayout mHeadView;//header
private TextView mTipsTextView;//header 标题
private ImageView mArrowImageView;//动画图片
private ProgressBar mProgressBar;
private RotateAnimation mAnimation;//逆时针180
private RotateAnimation mReverseAnimation;//顺时针180
private boolean mIsRecored;
private int mHeadContentWidth;
private int mHeadContentHeight;
private int mStartY;
private int mFirstItemIndex;
private int mState;
private boolean mIsBack;
private boolean mISRefreshable;
private OnRefreshListener mRefreshListener;
public RefreshCheckBoxListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {
mInflater = LayoutInflater.from(context);
mHeadView = (LinearLayout) mInflater.inflate(R.layout.item_head_of_listview, null);
mArrowImageView = (ImageView) mHeadView.findViewById(R.id.head_arrowImageView);
mProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressBar);
mTipsTextView = (TextView) mHeadView.findViewById(R.id.head_tipsTextView);
measureView(mHeadView);//测量
mHeadContentHeight = mHeadView.getMeasuredHeight();
System.out.println("mHeadContentHeight = " + mHeadContentHeight);
mHeadContentWidth = mHeadView.getMeasuredWidth();
System.out.println("mHeadContentWidth = " + mHeadContentWidth);
mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0);
mHeadView.invalidate();
addHeaderView(mHeadView, null, false);//将header添加到listView中
setOnScrollListener(this);
mAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mAnimation.setInterpolator(new LinearInterpolator());
mAnimation.setDuration(250);
mAnimation.setFillAfter(true);
mReverseAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseAnimation.setInterpolator(new LinearInterpolator());
mReverseAnimation.setDuration(250);
mReverseAnimation.setFillAfter(true);
mState = DONE;
mISRefreshable = false;
}
private void measureView(View child) {
android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
System.out.println("params = " + params);
if(params == null) {
params = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
System.out.println("lpWidth = " + params.width);
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0+0, params.width);
System.out.println("childWidthSpec = " + childWidthSpec);
int lpHeight = params.height;
System.out.println("lpHeight = " + lpHeight);
int childHeightSpec;
if(lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.UNSPECIFIED);
}
System.out.println("childHeightSpec = " + childHeightSpec);
child.measure(childWidthSpec, childHeightSpec);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mFirstItemIndex = firstVisibleItem;
}
public interface OnRefreshListener {
void onRefresh();
}
private void onRefresh() {
if(mRefreshListener != null) {
mRefreshListener.onRefresh();
}
}
public void onRefreshComplete() {
mState = DONE;
changeHeaderViewByState();
}
public void setonRefreshListener(OnRefreshListener onRefreshListener) {
this.mRefreshListener = onRefreshListener;
mISRefreshable = true;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(mISRefreshable) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if(mFirstItemIndex == 0 && !mIsRecored) {
mIsRecored = true;
mStartY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_UP:
if(mState != REFRESHING && mState != LOADING) {
if(mState == DONE) {
}
if(mState == PULL_TO_REFRESH) {
mState = DONE;
changeHeaderViewByState();
}
if(mState == RELEASE_TO_REFRESH) {
mState = REFRESHING;
changeHeaderViewByState();
onRefresh();
}
}
mIsBack = false;
mIsRecored = false;
break;
case MotionEvent.ACTION_MOVE:
int tempY = (int) ev.getY();
if(!mIsRecored && mFirstItemIndex == 0) {
mIsRecored = true;
mStartY = tempY;
}
if(mState != REFRESHING && mIsRecored && mState != LOADING) {
if(mState == RELEASE_TO_REFRESH) {
setSelection(0);
if((tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0) {
mState = PULL_TO_REFRESH;
changeHeaderViewByState();
} else if(tempY - mStartY <= 0) {
mState = DONE;
changeHeaderViewByState();
}
}
if(mState == PULL_TO_REFRESH) { //下拉长度达到header的3倍高度时,进行释放刷新提示
setSelection(0);
if((tempY - mStartY)/RADIO >= mHeadContentHeight) {
mState = RELEASE_TO_REFRESH;
mIsBack = true;
changeHeaderViewByState();
}
} else if(tempY - mStartY <= 0) { //上划情况
mState = DONE;
changeHeaderViewByState();
}
if(mState == DONE) {//如果下拉重置state状态
if(tempY - mStartY > 0) {
mState = PULL_TO_REFRESH;
changeHeaderViewByState();
}
}
if(mState == PULL_TO_REFRESH) {//释放刷新
mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
}
if(mState == RELEASE_TO_REFRESH) {
mHeadView.setPadding(0, (tempY - mStartY)/RADIO - mHeadContentHeight, 0, 0);
}
}
break;
default:
break;
}
}
return super.onTouchEvent(ev);
}
private void changeHeaderViewByState() {//control the state of header
switch (mState) {
case PULL_TO_REFRESH:
mProgressBar.setVisibility(GONE);
mTipsTextView.setVisibility(VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(VISIBLE);
if(mIsBack) {
mIsBack = false;
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mReverseAnimation);
mTipsTextView.setText("isBack is true!!!");
} else {
mTipsTextView.setText("isBack is false!!!");
}
break;
case DONE:
mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0);
mProgressBar.setVisibility(GONE);
mArrowImageView.clearAnimation();
mArrowImageView.setImageResource(R.drawable.upward);
mTipsTextView.setText("加载完毕");
break;
case REFRESHING:
mHeadView.setPadding(0, 0, 0, 0);
mProgressBar.setVisibility(VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(GONE);
mTipsTextView.setText("努力加载中……");
break;
case RELEASE_TO_REFRESH:
mArrowImageView.setVisibility(VISIBLE);
mProgressBar.setVisibility(GONE);
mTipsTextView.setVisibility(VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mAnimation);
mTipsTextView.setText("释放刷新");
break;
default:
break;
}
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
}
}
package com.lei.diyrefresh;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RefreshListViewAdapter extends BaseAdapter {
private static String TAG="RefreshListViewAdapter";
private Context context;
private List<String> stringList;
public static Map<Integer, Boolean> isSelected;
public RefreshListViewAdapter(Context context, List<String> list){
this.context = context;
this.stringList = list;
setStart();
}
private void setStart(){
isSelected = new HashMap<Integer, Boolean>();
for (int i = 0; i < stringList.size(); i++) {
isSelected.put(i, false);
}
}
@Override
public int getCount() {
return stringList.size();
}
@Override
public Object getItem(int position) {
return stringList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String bulb=stringList.get(position);
View view;
ViewHolder viewHolder;
if (convertView==null){
view= LayoutInflater.from(context).inflate(R.layout.item_pull_to_refresh_with_checkbox,null);
viewHolder=new ViewHolder();
viewHolder.tvName=(TextView)view.findViewById(R.id.tvRouterName);
viewHolder.tvContent=(TextView)view.findViewById(R.id.tvContent);
viewHolder.cBox=(CheckBox)view.findViewById(R.id.cBox);
view.setTag(viewHolder);//store up viewHolder
}else {
view=convertView;
viewHolder=(ViewHolder)view.getTag();
}
viewHolder.tvContent.setText("position: " + position);
viewHolder.tvName.setText(bulb);
viewHolder.cBox.setChecked(isSelected.get(position));//isSelected.get(position)
Log.i(TAG, "position: " + position + " " + isSelected.get(position));
/* if ((position%2)==1){
viewHolder.cBox.setChecked(true);//isSelected.get(position)
Log.i(TAG, "单数 " + position);
}else {
viewHolder.cBox.setChecked(false);//isSelected.get(position)
Log.i(TAG, "偶数 "+position);
}
*/
return view;
}
public class ViewHolder{
TextView tvName,tvContent;
public CheckBox cBox;
}
}
package com.lei.diyrefresh;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static String TAG="MainActivity";
RefreshCheckBoxListView mRefreshCheckBoxListView;
RefreshListViewAdapter listViewAdapter;
List<String> list=new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getData();
initView();
}
private void initView(){
mRefreshCheckBoxListView=(RefreshCheckBoxListView)findViewById(R.id.lvPull);
listViewAdapter=new RefreshListViewAdapter(this,list);
mRefreshCheckBoxListView.setAdapter(listViewAdapter);
mRefreshCheckBoxListView.setItemsCanFocus(false);
mRefreshCheckBoxListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mRefreshCheckBoxListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
RefreshListViewAdapter.ViewHolder vHolder = (RefreshListViewAdapter.ViewHolder) view.getTag();
//在每次获取点击的item时将对应的checkbox状态改变,同时修改map的值。
vHolder.cBox.toggle();
RefreshListViewAdapter.isSelected.put(position - 1, vHolder.cBox.isChecked());
//Log.i(TAG, "position: " + position);
}
});
mRefreshCheckBoxListView.setonRefreshListener(new RefreshCheckBoxListView.OnRefreshListener() {
@Override
public void onRefresh() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
listViewAdapter.notifyDataSetChanged();
mRefreshCheckBoxListView.onRefreshComplete();
}
}.execute();
}
});
}
private void getData(){
for (int i=0;i<20;i++){
list.add(new String("第 "+i));
}
}
}
注意事项:因为之前代码添加head到listview中,导致listview增加了一项,所以在这里position要减1RefreshListViewAdapter.isSelected.put(position - 1, vHolder.cBox.isChecked());
主布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lei.diyrefresh.MainActivity">
<com.lei.diyrefresh.RefreshCheckBoxListView
android:id="@+id/lvPull"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.lei.diyrefresh.RefreshCheckBoxListView>
</RelativeLayout>