這篇文章主要為大家展示了“Android中帶加載效果的下拉刷新上拉加載更多怎么實(shí)現(xiàn)”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Android中帶加載效果的下拉刷新上拉加載更多怎么實(shí)現(xiàn)”這篇文章吧。
成都創(chuàng)新互聯(lián)公司專注于中大型企業(yè)的成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)和網(wǎng)站改版、網(wǎng)站營(yíng)銷服務(wù),追求商業(yè)策劃與數(shù)據(jù)分析、創(chuàng)意藝術(shù)與技術(shù)開發(fā)的融合,累計(jì)客戶超過千家,服務(wù)滿意度達(dá)97%。幫助廣大客戶順利對(duì)接上互聯(lián)網(wǎng)浪潮,準(zhǔn)確優(yōu)選出符合自己需要的互聯(lián)網(wǎng)運(yùn)用,我們將一直專注品牌網(wǎng)站建設(shè)和互聯(lián)網(wǎng)程序開發(fā),在前進(jìn)的路上,與客戶一起成長(zhǎng)!
效果圖:
實(shí)現(xiàn)過程
首先是自定義屬性,attrs.xml中定義頭部的高度和上下的padding。
####attrs.xml####
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="PPRefreshView_header"> <attr name="header_height" format="dimension"/> <attr name="header_padding" format="dimension"/> </declare-styleable> </resources>
然后是頭部的文件,里面放了一個(gè)TextView和一個(gè)圖片
header_layout.xml####
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="vertical"> <ImageView android:src="@mipmap/down" android:layout_centerVertical="true" android:id="@+id/icon" android:layout_width="20dp" android:layout_height="20dp" android:layout_toLeftOf="@+id/text" android:layout_marginRight="5dp"/> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="下拉刷新" /> </RelativeLayout>
然后是布局文件,讓PPRefreshView作為父布局,下面可以放AbsListView的子類。
activity_main.xml####
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ppRefreshView="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" tools:context="top.greendami.greendami.MainActivity"> <top.greendami.greendami.PPRefreshView ppRefreshView:header_height="50dp" ppRefreshView:header_padding="10dp" android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#d4d4d4"> <ListView android:background="@color/white" android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> </top.greendami.greendami.PPRefreshView> </RelativeLayout>
最后是重點(diǎn),下拉刷新的控件。
"精心"準(zhǔn)備了一張圖
####PPRefreshView.java####
package top.greendami.greendami; package com.allrun.arsmartelevatorformanager.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.TextView; import com.allrun.arsmartelevatorformanager.R; import com.allrun.arsmartelevatorformanager.util.DPUnitUtil; /** * Created by GreendaMI on 2017/3/21. */ public class PPRefreshView extends ViewGroup { Context context; RecyclerView mListView; PPView mPPView; View header; TextView title; ImageView mImage;//箭頭 int listTop = 0; float headerHeight = 10 + 30 + 10;//header的高度,上留白 + 文字(PPVIew)高度 + 下留白 float headerpadding = 10;//上留白,下留白 private int mYDown, mLastY; //最短滑動(dòng)距離 int a = 0; RotateAnimation mRotateAnimation; int state = 0; //0,正常;1,下拉;2,松開 public void setPPRefreshViewListener(PPRefreshViewListener mPPRefreshViewListener) { this.mPPRefreshViewListener = mPPRefreshViewListener; } PPRefreshViewListener mPPRefreshViewListener; public PPRefreshView(Context context) { super(context); this.context = context; } public PPRefreshView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; //px轉(zhuǎn)dp a = DPUnitUtil.px2dip(context,ViewConfiguration.get(context).getScaledDoubleTapSlop()); TypedArray b = context.obtainStyledAttributes(attrs, R.styleable.PPRefreshView_header); headerHeight = b.getDimension(R.styleable.PPRefreshView_header_header_height, 100); headerpadding = b.getDimension(R.styleable.PPRefreshView_header_header_padding, 10); b.recycle(); initAnima(); } private void initAnima() { //箭頭旋轉(zhuǎn) mRotateAnimation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateAnimation.setFillAfter(true); mRotateAnimation.setDuration(200); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mPPView != null) { mPPView.measure(widthMeasureSpec,(int) (headerHeight- 2 * headerpadding)); } if (header != null) { header.measure(widthMeasureSpec, (int) (headerHeight- 2 * headerpadding)); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mListView == null && getChildCount() == 1) { mListView = (RecyclerView)getChildAt(0); mListView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if(!recyclerView.canScrollVertically(1)){ //添加外部回調(diào) if(mPPRefreshViewListener != null){ mPPRefreshViewListener.LoadMore(); } } } }); } if (mListView != null) { mListView.layout(l, listTop, getMeasuredWidth(), b); } if (mPPView != null) { //top:文字(PPVIew)高度 + 下留白 mPPView.layout(l, (int)(listTop - headerHeight + headerpadding), r, listTop); } if (header != null) { //top:文字(PPView)高度 + 下留白 header.layout(l, (int)(listTop - headerHeight + headerpadding), r, listTop); } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); //松開手指,list回到頂部 if (state == 2) { listTop = listTop - 25; if (listTop < headerHeight) { listTop = (int)headerHeight; } requestLayout(); } //刷新完畢,關(guān)閉header if (state == 0 && listTop > 0) { listTop = listTop - 25; if (listTop < 0) { listTop = 0; } requestLayout(); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: // 按下 mYDown = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: // 移動(dòng) mLastY = (int) event.getRawY(); if (!mListView.canScrollVertically(-1) && mLastY > mYDown &&(mLastY - mYDown) > a) { state = 1; listTop = mLastY - mYDown; if (mPPView != null) { removeView(mPPView); mPPView = null; } if (header == null) { header = LayoutInflater.from(context).inflate(R.layout.header_layout, null, false); title = ((TextView) header.findViewById(R.id.text)); mImage = ((ImageView) header.findViewById(R.id.icon)); addView(header); } if (title != null && (mLastY - mYDown) > a * 2f) { title.setText("松開刷新"); if (mImage.getAnimation() == null) { mImage.startAnimation(mRotateAnimation); } } if (title != null && (mLastY - mYDown) < a * 2f) { title.setText("下拉刷新"); mImage.setImageResource(R.mipmap.down); } requestLayout(); //已經(jīng)判斷是下拉刷新,攔截手勢(shì) return false; } break; case MotionEvent.ACTION_UP: // 抬起 // if (canLoad()) { // loadData(); // } //松手的時(shí)候,把文字標(biāo)題去掉 if (header != null) { removeView(header); header = null; } //如果之前是下拉狀態(tài),就添加PPVIew if (mPPView == null && state == 1) { //添加外部回調(diào) if(mPPRefreshViewListener != null){ mPPRefreshViewListener.onRefresh(); } mPPView = new PPView(context); addView(mPPView); mYDown = 0; mLastY = 0; state = 2; requestLayout(); } break; default: break; } return super.dispatchTouchEvent(event); } /** * 收起下拉刷新的header,刷新結(jié)束 */ public void RefreshOver() { if (mPPView != null) { removeView(mPPView); mPPView = null; } if (header != null) { removeView(header); header = null; title = null; mImage = null; } state = 0; } public void setRefreshing(boolean b) { if(!b){ state = 0; postInvalidate(); }else{ state = 2; postInvalidate(); } } public interface PPRefreshViewListener{ void onRefresh(); void LoadMore(); } }
主要思路是監(jiān)聽手勢(shì)的滑動(dòng)距離,如果ListView已經(jīng)劃到頂部,則ListView跟隨手指位置,并添加Header。放開手指后,ListView慢慢回到頂部。
外部回調(diào)。監(jiān)聽下拉和上拉。
mSwipeRefreshLayout.setPPRefreshViewListener(new PPRefreshView.PPRefreshViewListener() { @Override public void onRefresh() { Toast.makeText(MainActivity.this,"親,刷新了",Toast.LENGTH_SHORT).show(); data.add("測(cè)試數(shù)據(jù)100"); mAdapter.notifyDataSetChanged(); } @Override public void LoadMore() { Toast.makeText(MainActivity.this,"加載更多",Toast.LENGTH_SHORT).show(); } }); refreshLayout.setRefreshing(false);;//刷新完畢
差點(diǎn)忘了粘連小球的View。
####PPView.java#### package top.greendami.greendami; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; /** * Created by GreendaMi on 2017/3/17. */ public class PPView extends View { String TAG = "PPView"; //動(dòng)畫開關(guān) boolean isLoading = true; Context mContext; private int mWidth = 100; private int mheight = 100; public int mColor; public Paint mPaint = new Paint(); float time = 0; //小球與中間打球的最遠(yuǎn)距離 float distance = 100; public PPView(Context context) { super(context); mContext = context; mColor = context.getResources().getColor(R.color.colorPrimary); init(); } public PPView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mContext = context; mColor = context.getResources().getColor(R.color.colorPrimary); init(); } private void init() { mPaint.setAntiAlias(true); mPaint.setColor(mColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); //寬度至少是高度的4倍 if (widthSpecSize < 4 * heightSpecSize) { widthSpecSize = 4 * heightSpecSize; } mWidth = widthSpecSize; mheight = heightSpecSize; distance = 1.2f * mheight; setMeasuredDimension(widthSpecSize, heightSpecSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isLoading) { //大圓半徑 float bigR = mheight * 0.32f + mheight * 0.03f * Math.abs((float) Math.sin(Math.toRadians(time))); float smallR = mheight * 0.17f + mheight * 0.03f * Math.abs((float) Math.cos(Math.toRadians(time))); float bigx = (getWidth()) / 2; //畫中間大圓 canvas.drawCircle(bigx, mheight / 2, bigR, mPaint); float smalx = getSmallCenterX(); //畫小圓 canvas.drawCircle(smalx, mheight / 2, smallR, mPaint); //畫鏈接 //小球在右側(cè) if (smalx > bigx) { Path path = new Path(); //上面的貝塞爾曲線的第一個(gè)點(diǎn),在大圓身上 float x1 = bigx + bigR * (float) Math.cos(Math.toRadians(time)); float y1 = mheight / 2 - bigR * (float) Math.sin(Math.toRadians(time)); if (y1 > mheight / 2 - smallR) { y1 = mheight / 2 - smallR; x1 = bigx + (float) (Math.sqrt(bigR * bigR - smallR * smallR)); } //上面的貝塞爾曲線的第三個(gè)點(diǎn),在小圓身上 float x2 = smalx - smallR * (float) Math.cos(Math.toRadians(time)); float y2 = mheight / 2 - smallR * (float) Math.sin(Math.toRadians(time)); if (y2 > mheight / 2 - smallR * 0.8) { y2 = mheight / 2 - smallR * 0.8f; x2 = smalx - smallR * (float) (Math.sqrt(1-0.64f)); } //下面的貝塞爾曲線的第三個(gè)點(diǎn),在小圓身上 float x3 = smalx - smallR * (float) Math.cos(Math.toRadians(time)); float y3 = mheight / 2 + smallR * (float) Math.sin(Math.toRadians(time)); if (y3 < mheight / 2 + smallR * 0.8) { y3 = mheight / 2 + smallR * 0.8f; x3 = smalx - smallR * (float) (Math.sqrt(1-0.64f)); } //下面的貝塞爾曲線的第一個(gè)點(diǎn),在大圓身上 float x4 = bigx + bigR * (float) Math.cos(Math.toRadians(time)); float y4 = mheight / 2 + bigR * (float) Math.sin(Math.toRadians(time)); if (y4 < mheight / 2 + smallR) { y4 = mheight / 2 + smallR; x4 = bigx + (float) (Math.sqrt(bigR * bigR - smallR * smallR)); } path.moveTo(x1, y1); path.quadTo((bigx + smalx) / 2, mheight / 2, x2, y2); // 繪制貝賽爾曲線(Path) path.lineTo(x3, y3); path.quadTo((bigx + smalx) / 2, mheight / 2, x4, y4); canvas.drawPath(path, mPaint); } //小球在左側(cè) if (smalx < bigx) { Path path = new Path(); float x1 = bigx + bigR * (float) Math.cos(Math.toRadians(time)); float y1 = mheight / 2 - bigR * (float) Math.sin(Math.toRadians(time)); if (y1 > mheight / 2 - smallR) { y1 = mheight / 2 - smallR; x1 = bigx - (float) (Math.sqrt(bigR * bigR - smallR * smallR)); } float x2 = smalx - smallR * (float) Math.cos(Math.toRadians(time)); float y2 = mheight / 2 - smallR * (float) Math.sin(Math.toRadians(time)); if (y2 > mheight / 2 - smallR * 0.8) { y2 = mheight / 2 - smallR * 0.8f; x2 = smalx + smallR * (float) (Math.sqrt(1-0.64f)); } float x3 = smalx - smallR * (float) Math.cos(Math.toRadians(time)); float y3 = mheight / 2 + smallR * (float) Math.sin(Math.toRadians(time)); if (y3 < mheight / 2 + smallR * 0.8) { y3 = mheight / 2 + smallR * 0.8f; x3 = smalx + smallR * (float) (Math.sqrt(1-0.64f)); } float x4 = bigx + bigR * (float) Math.cos(Math.toRadians(time)); float y4 = mheight / 2 + bigR * (float) Math.sin(Math.toRadians(time)); if (y4 < mheight / 2 + smallR) { y4 = mheight / 2 + smallR; x4 = bigx - (float) (Math.sqrt(bigR * bigR - smallR * smallR)); } path.moveTo(x1, y1); path.quadTo((bigx + smalx) / 2, mheight / 2, x2, y2); // 繪制貝賽爾曲線(Path) path.lineTo(x3, y3); path.quadTo((bigx + smalx) / 2, mheight / 2, x4, y4); canvas.drawPath(path, mPaint); } postInvalidate(); } } //計(jì)算小球的X坐標(biāo) private float getSmallCenterX() { //此處控制速度 time = time + 4f; return mWidth / 2 + distance * (float) Math.cos(Math.toRadians(time)); } }
兩張素材
以上是“Android中帶加載效果的下拉刷新上拉加載更多怎么實(shí)現(xiàn)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
文章標(biāo)題:Android中帶加載效果的下拉刷新上拉加載更多怎么實(shí)現(xiàn)
本文地址:http://www.rwnh.cn/article12/jiesgc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、網(wǎng)站改版、商城網(wǎng)站、網(wǎng)站設(shè)計(jì)、網(wǎng)站設(shè)計(jì)公司、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)