android自定义带下拉刷新和Checkbox的ListView

(尊重原创转载请说明来处,谢谢)

       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>

自定义listview代码:

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);
    }
}

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;
    }

}

MainActivity:

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要减1
 RefreshListViewAdapter.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>


好了,思路和代码差不多就这些了,一次的编辑不可能完善,有什么问题大家可以指出来,我还会继续去细化完善

你可能感兴趣的:(android自定义带下拉刷新和Checkbox的ListView)