自定义控件之上拉刷新

自定义控件所需要的布局文件:xlistview_footer.xml

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

    <RelativeLayout
        android:id="@+id/xlistview_footer_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="5dp" >

        <ProgressBar
            android:id="@+id/xlistview_footer_progressbar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:visibility="invisible" />

        <TextView
            android:id="@+id/xlistview_footer_hint_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="查看更多" />
    </RelativeLayout>

</LinearLayout>

自定义控件:XListView.java

/*
 * @(#)XListView.java
 *
 * Copyright 2015, .....
 */
package com.example.test_my_x_list_view.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Scroller;
import android.widget.TextView;

import com.example.test_my_x_list_view.R;

/**
 * XListView:上拉刷新
 *
 * <p>
 * <b>History:</b>
 * <table border="1">
 * <tr>
 * <th>Date</th>
 * <th>Operator</th>
 * <th>Memo</th>
 * </tr>
 * <tr>
 * <td>2015-5-13</td>
 * <td>Zjc</td>
 * <td>Create</td>
 * </tr>
 * </table>
 *
 * @author Zjc
 *
 * @version 1.0.0
 * @since 1.0.0
 */
public class XListView extends ListView implements OnScrollListener {

    private float mLastY = -1;
    private Scroller mScroller;
    private OnScrollListener mScrollListener;
    private IXListViewListener mListViewListener;
    public XListViewFooter mFooterView;
    private boolean mEnablePullLoad;
    private boolean mPullLoading;
    private boolean mIsFooterReady = false;
    private int mTotalItemCount;
    private int mScrollBack;
    private final static int SCROLLBACK_HEADER = 0;
    private final static int SCROLLBACK_FOOTER = 1;
    private final static int SCROLL_DURATION = 400;
    private final static int PULL_LOAD_MORE_DELTA = 50;
    private final static float OFFSET_RADIO = 1.8f;

    /**
     * 默认构造方法
     *
     * @param context
     * @param attrs
     */
    public XListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initWithContext(context);
    }

    /**
     * 默认构造方法
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public XListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initWithContext(context);
    }

    /**
     * 默认构造方法
     *
     * @param context
     */
    public XListView(Context context) {
        super(context);
        initWithContext(context);
    }

    /**
     * 根据上下文进行初始化
     *
     * @param context
     */
    private void initWithContext(Context context) {
        mScroller = new Scroller(context, new DecelerateInterpolator());
        super.setOnScrollListener(this);
        mFooterView = new XListViewFooter(context);
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        if (mIsFooterReady == false) {
            mIsFooterReady = true;
            addFooterView(mFooterView);
            // addHeaderView(mFooterView);
        }
        super.setAdapter(adapter);
    }

    /**
     * enable or disable pull up load more feature.
     *
     * @param enable
     */
    public void setPullLoadEnable(boolean enable) {
        mEnablePullLoad = enable;
        if (!mEnablePullLoad) {
            mFooterView.hide();
            mFooterView.setOnClickListener(null);
        } else {
            mPullLoading = false;
            mFooterView.show();
            mFooterView.setState(XListViewFooter.STATE_NORMAL);
            // both "pull up" and "click" will invoke load more.
            mFooterView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    startLoadMore();
                }
            });
        }
    }

    private void startLoadMore() {
        mPullLoading = true;
        // 设置底部状态
        mFooterView.setState(XListViewFooter.STATE_LOADING);
        if (mListViewListener != null) {
            mListViewListener.onLoadMore();
        }
    }

    /**
     * stop load more, reset footer view.
     */
    public void stopLoadMore() {
        if (mPullLoading == true) {
            mPullLoading = false;
            mFooterView.setState(XListViewFooter.STATE_NORMAL);
        }
    }

    private void updateFooterHeight(float delta) {
        int height = mFooterView.getBottomMargin() + (int) delta;
        if (mEnablePullLoad && !mPullLoading) {
            if (height > PULL_LOAD_MORE_DELTA) {
                mFooterView.setState(XListViewFooter.STATE_READY);
            } else {
                mFooterView.setState(XListViewFooter.STATE_NORMAL);
            }
        }
        mFooterView.setBottomMargin(height);

    }

    private void invokeOnScrolling() {
        if (mScrollListener instanceof OnXScrollListener) {
            OnXScrollListener l = (OnXScrollListener) mScrollListener;
            l.onXScrolling(this);
        }
    }

    private void resetFooterHeight() {
        int bottomMargin = mFooterView.getBottomMargin();
        if (bottomMargin > 0) {
            mScrollBack = SCROLLBACK_FOOTER;
            mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,
                    SCROLL_DURATION);
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mLastY == -1) {
            mLastY = ev.getRawY();
        }
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastY = ev.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            final float deltaY = ev.getRawY() - mLastY;
            mLastY = ev.getRawY();
            if (getLastVisiblePosition() == mTotalItemCount - 1
                    && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
                updateFooterHeight(-deltaY / OFFSET_RADIO);
            }
            break;
        default:
            mLastY = -1; // reset
            if (getLastVisiblePosition() == mTotalItemCount - 1) {
                // invoke load more.
                if (mEnablePullLoad
                        && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
                    startLoadMore();
                }
                resetFooterHeight();
            }
            break;
        }
        return super.onTouchEvent(ev);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            if (mScrollBack != SCROLLBACK_HEADER) {
                mFooterView.setBottomMargin(mScroller.getCurrY());
            }
            postInvalidate();
            invokeOnScrolling();
        }
        super.computeScroll();
    }

    @Override
    public void setOnScrollListener(OnScrollListener l) {
        mScrollListener = l;
    }

    public interface OnXScrollListener extends OnScrollListener {
        public void onXScrolling(View view);
    }

    public interface IXListViewListener {
        public void onLoadMore();
    }

    public void setXListViewListener(IXListViewListener l) {
        mListViewListener = l;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * android.widget.AbsListView.OnScrollListener#onScrollStateChanged(android
     * .widget.AbsListView, int)
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (mScrollListener != null) {
            mScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget.
     * AbsListView, int, int, int)
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        mTotalItemCount = totalItemCount;
        if (mScrollListener != null) {
            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
                    totalItemCount);
        }
    }

    protected class XListViewFooter extends LinearLayout {
        public final static int STATE_NORMAL = 0;
        public final static int STATE_READY = 1;
        public final static int STATE_LOADING = 2;
        private Context mContext;
        private View mContentView;
        private View mProgressBar;
        private TextView mHintView;

        public XListViewFooter(Context context) {
            super(context);
            initView(context);
        }

        public XListViewFooter(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }

        public void setState(int state) {
            mHintView.setVisibility(View.INVISIBLE);
            mProgressBar.setVisibility(View.INVISIBLE);
            mHintView.setVisibility(View.INVISIBLE);
            if (state == STATE_READY) {
                mHintView.setVisibility(View.VISIBLE);
                mHintView.setText("松开载入更多");
            } else if (state == STATE_LOADING) {
                mProgressBar.setVisibility(View.VISIBLE);
            } else {
                mHintView.setVisibility(View.VISIBLE);
                mHintView.setText("查看更多");
            }
        }

        public void setBottomMargin(int height) {
            if (height < 0)
                return;
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
                    .getLayoutParams();
            lp.bottomMargin = height;
            mContentView.setLayoutParams(lp);
        }

        public int getBottomMargin() {
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
                    .getLayoutParams();
            return lp.bottomMargin;
        }

        /**
         * normal status
         */
        public void normal() {
            mHintView.setVisibility(View.VISIBLE);
            mProgressBar.setVisibility(View.GONE);
        }

        /**
         * loading status
         */
        public void loading() {
            mHintView.setVisibility(View.GONE);
            mProgressBar.setVisibility(View.VISIBLE);
        }

        /**
         * hide footer when disable pull load more
         */
        public void hide() {
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
                    .getLayoutParams();
            lp.height = 0;
            mContentView.setLayoutParams(lp);
        }

        /**
         * show footer
         */
        public void show() {
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
                    .getLayoutParams();
            lp.height = LayoutParams.WRAP_CONTENT;
            mContentView.setLayoutParams(lp);
        }

        @SuppressWarnings("deprecation")
        private void initView(Context context) {
            mContext = context;
            LinearLayout moreView = (LinearLayout) LayoutInflater
                    .from(mContext).inflate(R.layout.xlistview_footer, null);
            addView(moreView);
            moreView.setLayoutParams(new LinearLayout.LayoutParams(
                    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
            mContentView = moreView.findViewById(R.id.xlistview_footer_content);
            mProgressBar = moreView
                    .findViewById(R.id.xlistview_footer_progressbar);
            mHintView = (TextView) moreView
                    .findViewById(R.id.xlistview_footer_hint_textview);
        }

    }

}

MainActivity.java对控件的使用

package com.example.test_my_x_list_view;

import android.app.Activity;
import android.os.Bundle;

import com.example.test_my_x_list_view.widget.XListView;
import com.example.test_my_x_list_view.widget.XListView.IXListViewListener;
import com.example.test_my_x_list_view.widget.adapter.MyAdapter;

public class MainActivity extends Activity {

    /** 页面控件 */
    private XListView x_list_view;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        x_list_view = (XListView) findViewById(R.id.x_list_view);
        // 设置可加载更多
        x_list_view.setPullLoadEnable(true);
        // 设置上拉刷新监听
        x_list_view.setXListViewListener(new IXListViewListener() {

            @Override
            public void onLoadMore() {
                // onLoaded();
            }
        });
        // 映射值
        x_list_view.setAdapter(new MyAdapter(getApplicationContext()));
    }
}

activity_main.xml

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

    <com.example.test_my_x_list_view.widget.XListView
        android:id="@+id/x_list_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>

适配器MyAdapter.java

/*
 * @(#)MyAdapter.java
 *
 * Copyright 2015, .....
 */
package com.example.test_my_x_list_view.widget.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

import com.example.test_my_x_list_view.R;

/**
 * ListView适配器
 *
 * <p>
 * <b>History:</b>
 * <table border="1">
 * <tr>
 * <th>Date</th>
 * <th>Operator</th>
 * <th>Memo</th>
 * </tr>
 * <tr>
 * <td>2015-5-6</td>
 * <td>Zjc</td>
 * <td>Create</td>
 * </tr>
 * </table>
 *
 * @author Zjc
 *
 * @version 1.0.0
 * @since 1.0.0
 */
public class MyAdapter extends BaseAdapter {

    private LayoutInflater inflater;

    /**
     * 默认构造方法
     */
    public MyAdapter(Context context) {
        this.inflater = LayoutInflater.from(context);
    }

    /*
     * (non-Javadoc)
     *
     * @see android.widget.Adapter#getCount()
     */
    @Override
    public int getCount() {
        return 28;
        // TODO 完成Adapter.getCount方法的构建或覆盖
    }

    /*
     * (non-Javadoc)
     *
     * @see android.widget.Adapter#getItem(int)
     */
    @Override
    public Object getItem(int position) {
        return null;
        // TODO 完成Adapter.getItem方法的构建或覆盖
    }

    /*
     * (non-Javadoc)
     *
     * @see android.widget.Adapter#getItemId(int)
     */
    @Override
    public long getItemId(int position) {
        return position;
        // TODO 完成Adapter.getItemId方法的构建或覆盖
    }

    /*
     * (non-Javadoc)
     *
     * @see android.widget.Adapter#getView(int, android.view.View,
     * android.view.ViewGroup)
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        convertView = inflater.inflate(R.layout.item, parent, false);
        return convertView;
    }
}

item.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"
    android:background="#607D8B" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:gravity="center_vertical" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="列表数据"
            android:textColor="#ffffff"
            android:textSize="14sp" />
    </LinearLayout>

</LinearLayout>
时间: 2024-08-16 19:19:51

自定义控件之上拉刷新的相关文章

android 自定义控件之下拉刷新源码详解

下拉刷新 是请求网络数据中经常会用的一种功能. 实现步骤如下: 1.新建项目   PullToRefreshDemo,定义下拉显示的头部布局pull_to_refresh_refresh.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmln

《React-Native系列》19、 ListView组件之上拉刷新(iOS和Android通用)

ReactNative提供了RefreshControl下拉刷新组件,但是没有提供上拉刷新组件,上拉刷新在App中是很常用的. 今天我们来实现一个iOS和Android通用的上拉刷新功能. 下面简要介绍下我实现的思路. 如果你对ListView的基础知识不是很清楚,建议先移步:<React-Native系列>16. RN组件之ListView 思路: 1.常量定义: const moreText = "加载完毕"; //foot显示的文案 //页码 var pageNum

1、ListView自定义控件下拉刷新(一)

1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:background="#f5f5f5" > 5 6 <zrc.widget.Zrc

Android之自定义控件-下拉刷新

实现效果: 图片素材:         --> 首先, 写先下拉刷新时的刷新布局 pull_to_refresh.xml: 1 <resources> 2 <string name="app_name">PullToRefreshTest</string> 3 <string name="pull_to_refresh">下拉可以刷新</string> 4 <string name="

Android自定义控件--下拉刷新的实现

我们在使用ListView的时候,很多情况下需要用到下拉刷新的功能.为了了解下拉刷新的底层实现原理,我采用自定义ListView控件的方式来实现效果. 实现的基本原理是:自定义ListView,给ListView加载头布局,然后动态的控制头布局的现实与隐藏.ListView初始化的时候,头布局是隐藏的,当手指往下拉的时候,根据手指移动的距离与头布局的高度的关系来控制头布局的显示.具体的控制思路详见后边的代码,代码中的注释很详细.先来看一下效果图:                     头布局的

自定义控件基础02_下拉刷新_侧拉菜单_自定义属性

自定义控件02 自定义控件 ①,纯粹自定义绘制 ②,在原生的基础上追加功能. 1,下拉刷新功能(继承ListView追加功能)(下拉刷新,加载更多,两个功能) 1.1 下拉刷新 ①创建一个类,继承ListView 创建自定义适配器,设置数据 额外:自定义控件会放到view包下 ②自定义控件的头(即下拉的时候显示的view) 推荐名称initHeaderView();在构造方法中初始化这个头 this.addHeaderView()//添加一个头布局的控件,在listView顶部添加一个头 头部u

Android—自定义控件实现ListView下拉刷新

这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的代码是自定的扎样的控件: package com.dhsr.smartID.view; import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import andr

Android自定义控件之仿汽车之家下拉刷新

关于下拉刷新的实现原理我在上篇文章Android自定义控件之仿美团下拉刷新中已经详细介绍过了,这篇文章主要介绍表盘的动画实现原理 汽车之家的下拉刷新分为三个状态: 第一个状态为下拉刷新状态(pull to refresh),在这个状态下是一个表盘随着下拉的距离动态改变指针的角度 第二个状态为放开刷新状态(release to refresh),在这个状态下是指针角度变化的一个动画 第一个状态的实现: 这个效果我们使用自定义View来实现,我们从汽车之家apk中拿到下拉刷新所用到的两张图片: 我们

Android自定义控件——ListView的下拉刷新与上拉加载

转载请注明出处:http://blog.csdn.net/allen315410/article/details/39965327 1.简介 无疑,在Android开发中,ListView是使用非常频繁的控件之一,ListView提供一个列表的容易,允许我们以列表的形式将数据展示到界面上,但是Google给我们提供的原生ListView的控件,虽然在功能上很强大,但是在用户体验和动态效果上,还是比较差劲的.为了改善用户体验,市面上纷纷出现了各种各样的自定义的ListView,他们功能强大,界面美