APP实用开发——定制自己的下拉刷新头

来源:互联网 发布:不同编程语言 做什么 编辑:程序博客网 时间:2024/06/08 05:56
  1. 功能介绍
    下拉刷新,几乎是每个 Android 应用都会需要的功能。 android-Ultra-Pull-To-Refresh (以下简称 UltraPTR )便是一个强大的 Andriod 下拉刷新框架。
    主要特点:
    (1).继承于 ViewGroup, Content 可以包含任何 View。
    (2).简洁完善的 Header 抽象,方便进行拓展,构建符合需求的头部。

GitHub
2. 总体设计
UltraPTR 总体设计比较简单清晰。
首先抽象出了两个接口,功能接口和 UI 接口。
PtrHandler 代表下拉刷新的功能接口,包含刷新功能回调方法以及判断是否可以下拉的方法。用户实现此接口来进行数据刷新工作。
PtrUIHandler 代表下拉刷新的 UI 接口,包含准备下拉,下拉中,下拉完成,重置以及下拉过程中的位置变化等回调方法。通常情况下, Header 需要实现此接口,来处理下拉刷新过程中头部 UI 的变化。
整个项目围绕核心类 PtrFrameLayout。 PtrFrameLayout 代表了一个下拉刷新的自定义控件。
PtrFrameLayout 继承自 ViewGroup,有且只能有两个子 View,头部 Header 和内容 Content。通常情况下 Header 会实现 PtrUIHandler 接口, Content 可以为任意的 View。
和所有的自定义控件一样, PtrFrameLayout 通过重写 onFinishInflate, onMeasure, onLayout 来确定控件大小和位置。通过重写 dispatchTouchEvent 来确定控件的下拉行为。

依赖

compile 'in.srain.cube:ultra-ptr:1.0.11'

定制
您可以添加PtrUIHandler到PtrFrameLayout以实现任何您想要的UI效果。

public  interface  PtrUIHandler {    / * *      *当内容视图已达到顶部并且刷新已完成时,将重置视图。     *      * @param frame * / public void onUIReset( PtrFrameLayout frame);         / * *      *准备加载     *      * @param frame * / public void onUIRefreshPrepare( PtrFrameLayout frame);         / * *      *执行刷新UI * / public void onUIRefreshBegin( PtrFrameLayout frame);         / * *      *刷新后执行UI * / public void onUIRefreshComplete( PtrFrameLayout frame);         public  void  onUIPositionChange(PtrFrameLayout  frame,boolean  isUnderTouch,byte  status,int  oldPosition,int  currentPosition,float  oldPercent,float  currentPercent);}}

栗子讲解

这里写图片描述
PtrClassicDefaultHeader 实现了 PtrUIHandler 接口。
经典样式的 Header 实现,可以作为我们实现自定义 Header 的参考,以下是具体实现。

@Overridepublic void onUIReset(PtrFrameLayout frame) {    resetView();    mShouldShowLastUpdate = true;    tryUpdateLastUpdateTime();}private void resetView() {    hideRotateView();    mProgressBar.setVisibility(INVISIBLE);}private void hideRotateView() {    mRotateView.clearAnimation();    mRotateView.setVisibility(INVISIBLE);}

重置 View,隐藏忙碌进度条,隐藏箭头 View,更新最后刷新时间。

@Overridepublic void onUIRefreshPrepare(PtrFrameLayout frame) {    mShouldShowLastUpdate = true;    tryUpdateLastUpdateTime();    mLastUpdateTimeUpdater.start();    mProgressBar.setVisibility(INVISIBLE);    mRotateView.setVisibility(VISIBLE);    mTitleTextView.setVisibility(VISIBLE);    if (frame.isPullToRefresh()) {        mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh));    } else {        mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down));    }}

准备刷新,隐藏忙碌进度条,显示箭头 View,显示文字,如果是下拉刷新,显示“下拉刷新”,如果是释放刷新,显示“下拉”。

@Overridepublic void onUIRefreshBegin(PtrFrameLayout frame) {    mShouldShowLastUpdate = false;    hideRotateView();    mProgressBar.setVisibility(VISIBLE);    mTitleTextView.setVisibility(VISIBLE);    mTitleTextView.setText(R.string.cube_ptr_refreshing);    tryUpdateLastUpdateTime();    mLastUpdateTimeUpdater.stop();}

开始刷新,隐藏箭头 View,显示忙碌进度条,显示文字,显示“加载中…”,更新最后刷新时间。

@Overridepublic void onUIRefreshComplete(PtrFrameLayout frame) {    hideRotateView();    mProgressBar.setVisibility(INVISIBLE);    mTitleTextView.setVisibility(VISIBLE);    mTitleTextView.setText(getResources().getString(R.string.cube_ptr_refresh_complete));    // update last update time    SharedPreferences sharedPreferences = getContext().getSharedPreferences(KEY_SharedPreferences, 0);    if (!TextUtils.isEmpty(mLastUpdateTimeKey)) {        mLastUpdateTime = new Date().getTime();        sharedPreferences.edit().putLong(mLastUpdateTimeKey, mLastUpdateTime).commit();    }}

刷新结束,隐藏箭头 View,隐藏忙碌进度条,显示文字,显示“更新完成”,写入最后刷新时间。

@Overridepublic void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int lastPos, int currentPos, float oldPercent, float currentPercent) {    final int mOffsetToRefresh = frame.getOffsetToRefresh();    if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {        if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {            crossRotateLineFromBottomUnderTouch(frame);            if (mRotateView != null) {                mRotateView.clearAnimation();                mRotateView.startAnimation(mReverseFlipAnimation);            }        }    } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {        if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {            crossRotateLineFromTopUnderTouch(frame);            if (mRotateView != null) {                mRotateView.clearAnimation();                mRotateView.startAnimation(mFlipAnimation);            }        }    }}

下拉过程中位置变化回调。
在拖动情况下,当下拉距离从 小于刷新高度到大于刷新高度 时,箭头 View 从向下,变成向上,同时改变文字显示。
当下拉距离从 大于刷新高度到小于刷新高度 时,箭头 View 从向上,变为向下,同时改变文字显示。

回调

ptrFrame.setPtrHandler(new PtrHandler() {    @Override    public void onRefreshBegin(PtrFrameLayout frame) {        frame.postDelayed(new Runnable() {            @Override            public void run() {                ptrFrame.refreshComplete();            }        }, 1800);    }    @Override    public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {        return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);    }});

案例2

这里写图片描述
首先我们要自己定义需要的头部布局

<?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="60dp"    android:orientation="horizontal"    android:background="@color/grby">    <LinearLayout        android:layout_width="0dp"        android:layout_height="60dp"        android:layout_weight="0.5">        <RelativeLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical">            <ImageView                android:id="@+id/iv_windmill"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_alignParentRight="true"                android:layout_centerHorizontal="true"                android:layout_centerVertical="true"                android:layout_marginRight="10dp"                android:background="@mipmap/header"/>        </RelativeLayout>    </LinearLayout>    <LinearLayout        android:layout_width="0dp"        android:layout_height="60dp"        android:layout_weight="1"        android:orientation="vertical">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="35dp"            android:orientation="vertical">            <TextView                android:id="@+id/tv_head_title"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:layout_marginLeft="30dp"                android:gravity="bottom"                android:text="下拉刷新"                android:textColor="#ffffff"                android:textSize="12sp" />        </LinearLayout>        <LinearLayout            android:layout_width="match_parent"            android:layout_height="25dp"            android:orientation="vertical">            <TextView                android:id="@+id/tv_head_time"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:gravity="center_vertical"                android:text="本次刷新时间 : 2015-10-23 12:00"                android:textColor="#ffffff"                android:textSize="10dp" />        </LinearLayout>    </LinearLayout></LinearLayout>
public class WindmillHeader extends FrameLayout implements PtrUIHandler {    private LayoutInflater inflater;    // 下拉刷新视图(头部视图)    private ViewGroup headView;    // 下拉刷新文字    private TextView tvHeadTitle;    // 下拉图标    private ImageView ivWindmill;    // 下拉刷新时间    private TextView tvHeadTime;    /** 保存上一次的刷新时间. */    private String lastRefreshTime = null; //动画    private WindmillDrawable drawable;    public WindmillHeader(Context context) {        this(context, null);    }    public WindmillHeader(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public WindmillHeader(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context);    }    /**     * 初始化     *     * @param context     */    private void init(Context context) {        inflater = LayoutInflater.from(context);        /**         * 头部         */        headView = (ViewGroup) inflater.inflate(R.layout.windmill_header, this, true);        ivWindmill = (ImageView) headView.findViewById(R.id.iv_windmill);        tvHeadTitle = (TextView) headView.findViewById(R.id.tv_head_title);        tvHeadTime= (TextView) findViewById(R.id.tv_head_time);        drawable = new WindmillDrawable(context, ivWindmill);        ivWindmill.setImageDrawable(drawable);    }    @Override    public void onUIReset(PtrFrameLayout ptrFrameLayout) {        tvHeadTitle.setText("下拉刷新");        drawable.stop();    }    @Override    public void onUIRefreshPrepare(PtrFrameLayout ptrFrameLayout) {        tvHeadTitle.setText("下拉刷新");        if(lastRefreshTime==null){            lastRefreshTime = TimeUtils.getCurrentTimeInString();            tvHeadTime.setText("当前刷新时间:" + lastRefreshTime);        }else{            tvHeadTime.setText("上次刷新时间:" + lastRefreshTime);        }    }    @Override    public void onUIRefreshBegin(PtrFrameLayout ptrFrameLayout) {        tvHeadTitle.setText("正在刷新");        tvHeadTime.setText("本次刷新时间:" + lastRefreshTime);        drawable.start();    }    @Override    public void onUIRefreshComplete(PtrFrameLayout ptrFrameLayout) {        ivWindmill.clearAnimation();        tvHeadTitle.setText("刷新完成");        tvHeadTime.setText("本次刷新时间:" + lastRefreshTime);    }    @Override    public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int lastPos, int currentPos, float oldPercent, float currentPercent) {        if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {            drawable.postRotation(currentPos - lastPos);            invalidate();        }        final int mOffsetToRefresh = frame.getOffsetToRefresh();        if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {            if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {                tvHeadTitle.setText("下拉刷新");                if(lastRefreshTime==null){                    lastRefreshTime = TimeUtils.getCurrentTimeInString();                    tvHeadTime.setText("当前刷新时间:" + lastRefreshTime);                }else{                    tvHeadTime.setText("上次刷新时间:" + lastRefreshTime);                }            }        } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {            if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {                tvHeadTitle.setText("松开刷新");                tvHeadTime.setText("上次刷新时间:" + lastRefreshTime);                lastRefreshTime = TimeUtils.getCurrentTimeInString();            }        }    }}
private WindmillHeader header;private void initView() { ptr= (PtrFrameLayout) findViewById(R.id.ptr_main); //创建自定义头部UI header = new WindmillHeader(this); /* 设置刷新头部view */ ptr.setHeaderView(header); //设置回调 ptr.addPtrUIHandler(header); ptr.disableWhenHorizontalMove(true); ptr.setPtrHandler(new PtrDefaultHandler() { @Override public void onRefreshBegin(final PtrFrameLayout frame) { new Handler().postDelayed(new Runnable() { @Override public void run() { // ptr.refreshComplete(); getdata(); } }, 3000); } @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } });  /* 延时100秒,自动刷新 */ ptr.postDelayed(new Runnable() { @Override public void run() { ptr.autoRefresh(false); } }, 100);}

案例3

public class WindmillHeader extends FrameLayout implements PtrUIHandler {    private LayoutInflater inflater;    // 下拉刷新视图(头部视图)    private ViewGroup headView;    // 下拉刷新文字    private TextView tvHeadTitle;    // 下拉图标    private ImageView ivWindmill;    private WindmillDrawable drawable;    public WindmillHeader(Context context) {        this(context, null);    }    public WindmillHeader(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public WindmillHeader(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context);    }    /**     * 初始化     *     * @param context     */    private void init(Context context) {        inflater = LayoutInflater.from(context);        /**         * 头部         */        headView = (ViewGroup) inflater.inflate(R.layout.windmill_header, this, true);        ivWindmill = (ImageView) headView.findViewById(R.id.iv_windmill);        tvHeadTitle = (TextView) headView.findViewById(R.id.tv_head_title);        drawable = new WindmillDrawable(context, ivWindmill);        ivWindmill.setImageDrawable(drawable);    }    @Override    public void onUIReset(PtrFrameLayout ptrFrameLayout) {        tvHeadTitle.setText("下拉刷新");        drawable.stop();    }    @Override    public void onUIRefreshPrepare(PtrFrameLayout ptrFrameLayout) {        tvHeadTitle.setText("下拉刷新");    }    @Override    public void onUIRefreshBegin(PtrFrameLayout ptrFrameLayout) {        tvHeadTitle.setText("正在刷新");        drawable.start();    }    @Override    public void onUIRefreshComplete(PtrFrameLayout ptrFrameLayout) {        ivWindmill.clearAnimation();        tvHeadTitle.setText("刷新完成");    }    @Override    public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int lastPos, int currentPos, float oldPercent, float currentPercent) {        if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {            drawable.postRotation(currentPos - lastPos);            invalidate();        }        final int mOffsetToRefresh = frame.getOffsetToRefresh();        if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {            if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {                tvHeadTitle.setText("下拉刷新");            }        } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {            if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {                tvHeadTitle.setText("松开刷新");            }        }    }}
public class WindmillDrawable extends Drawable implements Animatable {    private static final String TAG = WindmillDrawable.class.getSimpleName();    private Resources resources;    private Bitmap windmill;    private Matrix matrix;    private View parent;    private Animation animation;    private boolean isFirstDraw = true;    private boolean isAnimating;    public WindmillDrawable(Context context, View parent) {        resources = context.getResources();        windmill = BitmapFactory.decodeResource(resources, R.drawable.windmill);        matrix = new Matrix();        this.parent = parent;        animation = new RotateAnimation(360, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);        animation.setInterpolator(new LinearInterpolator());        animation.setDuration(800);        animation.setRepeatMode(Animation.RESTART);        animation.setRepeatCount(Animation.INFINITE);        animation.setFillAfter(true);    }    @Override    public void draw(Canvas canvas) {        if (isFirstDraw) {            isFirstDraw = false;            matrix.setTranslate((getBounds().width() - windmill.getWidth()) / 2, (getBounds().height() - windmill.getHeight()) / 2);        }        Paint p = new Paint();        canvas.drawBitmap(windmill, matrix, p);    }    @Override    public void setAlpha(int alpha) {    }    @Override    public void setColorFilter(ColorFilter cf) {    }    @Override    public int getOpacity() {        return 0;    }    public void postRotation(int degree) {        matrix.postRotate(degree, getBounds().exactCenterX(), getBounds().exactCenterY());        invalidateSelf();    }    @Override    public void start() {        parent.startAnimation(animation);        isAnimating = true;    }    @Override    public void stop() {        parent.clearAnimation();        isAnimating = false;    }    @Override    public boolean isRunning() {        return isAnimating;    }
 */public class ContentGridViewFragment extends Fragment {    private GridView gvMain;    private BaseAdapter adapter;    private PtrClassicFrameLayout ptr;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        View v = inflater.inflate(R.layout.fragment_content_grid_view, container, false);        initView(v);        return v;    }    private void initView(View v) {        gvMain = (GridView) v.findViewById(R.id.gv_main);        ptr = (PtrClassicFrameLayout) v.findViewById(R.id.ptr_main);        ptr.setPtrHandler(new PtrDefaultHandler() {            @Override            public void onRefreshBegin(PtrFrameLayout ptrFrameLayout) {                getData();                ptr.refreshComplete();            }        });        new Handler().postDelayed(new Runnable() {            @Override            public void run() {                ptr.autoRefresh();            }        }, 100);    }    private void getData() {        adapter = new GradViewAdapter(getActivity(), Constants.SMALL_IMAGE_URLS);        gvMain.setAdapter(adapter);    }}Contact GitHub API Training Shop Blog About

Demo

0 0