android 控制元件 下拉重新整理 phoenix 帶原始碼分析
向納什致敬,鳳凰城永遠的英雄!phoenix
Yalantis 致力於提供世界一流的 Android 和 iOS 應用開發服務,因一些
動畫很棒的開源庫為大家所熟知
Phoenix-Android 旨在提供一個簡單的可定製的下拉重新整理功能。
<com.hankkin.AnimationPullToRefreshDemo.PullToRefreshView android:id="@+id/pull_to_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/list_view" android:divider="@null" android:dividerHeight="0dp" android:fadingEdge="none" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.hankkin.AnimationPullToRefreshDemo.PullToRefreshView>
由此我們可以知道,在這個下拉重新整理並不是重寫了listview,而是在listview的外面套了一層佈局,也就是說listview被新增到了Phoenix上,那麼我們就能知道Phoenix其實就是一個viewgroup,到這裡就差不多知道要重寫那幾個方法了,自定義viewgroup的話也就onlayout、onmeasure、ontouchevent(如果是自定義view的話一般就重寫onmeasure、ondraw、ontouchevent),整理到這裡我們就可以來看看那原始碼了。
原始碼分析
構造方法
onmeasure
onlayout
頭部動畫效果實現
構造方法
首先看他的構造方法
public PullToRefreshView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RefreshView); final int type = a.getInteger(R.styleable.RefreshView_type, STYLE_SUN); a.recycle(); mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTotalDragDistance = Utils.convertDpToPixel(context, DRAG_MAX_DISTANCE); mRefreshView = new ImageView(context); setRefreshStyle(type); addView(mRefreshView); //保證ondraw會執行,如果是true的話ondraw不會執行 setWillNotDraw(false); ViewCompat.setChildrenDrawingOrderEnabled(this, true); }
public void setRefreshStyle(int type) {
setRefreshing(false);
switch (type) {
case STYLE_SUN:
mBaseRefreshView = new SunRefreshView(getContext(), this);
break;
default:
throw new InvalidParameterException("Type does not exist");
}
mRefreshView.setImageDrawable(mBaseRefreshView);
}
在這裡setRefreshStyle其實就可以直接看成是給頭部的imageview設定顯示的內容,然後將這個imageview新增到viewgroup中,另外的就是一寫引數的初始化。簡單的說就是在這個已經包了一個listview的viewgroup中再新增一個imageview。
onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ensureTarget();
if (mTarget == null)
return;
widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingRight() - getPaddingLeft(), MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);
mTarget.measure(widthMeasureSpec, heightMeasureSpec);
mRefreshView.measure(widthMeasureSpec, heightMeasureSpec);
}
private void ensureTarget() {
if (mTarget != null)
return;
if (getChildCount() > 0) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child != mRefreshView) {
mTarget = child;
mTargetPaddingBottom = mTarget.getPaddingBottom();
mTargetPaddingLeft = mTarget.getPaddingLeft();
mTargetPaddingRight = mTarget.getPaddingRight();
mTargetPaddingTop = mTarget.getPaddingTop();
}
}
}
}
一開始mTarget 是空的,然後到getChildCount方法,想一下這個時候這個viewgroup中也就兩個孩子,一個imageview,一個listview,ensureTarget的作用就是把listview的例項賦值給mTarget,以及給幾個padding賦值,隨後在onmeasure中設定imageview和listview與外層的viewgroup一樣大小。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
ensureTarget();
if (mTarget == null)
return;
int height = getMeasuredHeight();
int width = getMeasuredWidth();
int left = getPaddingLeft();
int top = getPaddingTop();
int right = getPaddingRight();
int bottom = getPaddingBottom();
mTarget.layout(left, top + mCurrentOffsetTop, left + width - right, top + height - bottom + mCurrentOffsetTop);
mRefreshView.layout(left, top, left + width - right, top + height - bottom);
}
imageview和listview放在相同的位置。
ontouchevent和onInterceptTouchEvent
如果不知道上面兩個方法的關係,可以去看看另一篇文章(http://blog.csdn.net/u012806692/article/details/50820070),首先重寫的是攔截的方法@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isEnabled() || canChildScrollUp() || mRefreshing) {
return false;
}
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
setTargetOffsetTop(0, true);
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mIsBeingDragged = false;
final float initialMotionY = getMotionEventY(ev, mActivePointerId);
if (initialMotionY == -1) {
return false;
}
mInitialMotionY = initialMotionY;
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER) {
return false;
}
final float y = getMotionEventY(ev, mActivePointerId);
if (y == -1) {
return false;
}
final float yDiff = y - mInitialMotionY;
if (yDiff > mTouchSlop && !mIsBeingDragged) {
mIsBeingDragged = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return mIsBeingDragged;
}
如果listview沒有滑到最頂部或者還在載入重新整理中就不執行之後的程式碼,直接返回false,否則記錄按下位置的y座標,注意這裡還有多點觸控的知識,這裡不理解可以先不用管。到了action_move之後,如果開始滑動(也就是大於mTouchSlop )就攔截touch事件不傳遞給子view,直接執行自己的ontouchevent方法,這裡我們先不管多點觸控相關的直接簡單理解下,接下來看ontouchevent事件
@Override
public boolean onTouchEvent(@NonNull MotionEvent ev) {
if (!mIsBeingDragged) {
return super.onTouchEvent(ev);
}
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
if (pointerIndex < 0) {
return false;
}
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = y - mInitialMotionY;
final float scrollTop = yDiff * DRAG_RATE;
mCurrentDragPercent = scrollTop / mTotalDragDistance;
if (mCurrentDragPercent < 0) {
return false;
}
float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent));
float extraOS = Math.abs(scrollTop) - mTotalDragDistance;
float slingshotDist = mTotalDragDistance;
float tensionSlingshotPercent = Math.max(0,
Math.min(extraOS, slingshotDist * 2) / slingshotDist);
float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
(tensionSlingshotPercent / 4), 2)) * 2f;
float extraMove = (slingshotDist) * tensionPercent / 2;
int targetY = (int) ((slingshotDist * boundedDragPercent) + extraMove);
mBaseRefreshView.setPercent(mCurrentDragPercent, true);
setTargetOffsetTop(targetY - mCurrentOffsetTop, true);
break;
}
case MotionEventCompat.ACTION_POINTER_DOWN:
final int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
if (mActivePointerId == INVALID_POINTER) {
return false;
}
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE;
mIsBeingDragged = false;
if (overScrollTop > mTotalDragDistance) {
setRefreshing(true, true);
} else {
mRefreshing = false;
animateOffsetToStartPosition();
}
mActivePointerId = INVALID_POINTER;
return false;
}
}
return true;
}
程式碼比較多一步步看,上面我們已經執行到了move,在ontouchevent的move中計算了滑動的百分比,頭部有個預設的最大值,看目前的滑動舉例是他的百分之幾,滑動距離超過最大值時取100%,在case action_move中的程式碼看著挺多,最重要的就最後的幾句,設定頭部的顯示百分比,還有listview的偏移(相當於magin),隨後主要看action_up,在擡起中判斷滑動距離是否到了載入的那個指定距離,如果足夠了就載入,不夠就直接回到初始位置,大致流程就是這樣。接下來看看imageview中的內容,就是一個
頭部動畫效果
其實就是imageview中的內容,一開始其實我們留下了一個問題,回想一下,在onlayout和onmeasure中我們設定的imageview的大小和顯示位置和listview的是一樣的,那麼兩個不就疊在一起了嗎?接下來看下imageview的內容是什麼就明白了。它的構造方法就不看了,就一堆變數的賦值,載入圖片等等,直接看自定義view最重要的draw方法@Override
public void draw(Canvas canvas) {
if (mScreenWidth <= 0) return;
final int saveCount = canvas.save();
canvas.translate(0, mTop);
canvas.clipRect(0, -mTop, mScreenWidth, mParent.getTotalDragDistance());
drawSky(canvas);
drawSun(canvas);
drawTown(canvas);
canvas.restoreToCount(saveCount);
}
其中將畫布移動到了mtop的位置,再看之前初始化的時候將其賦值為mTop = -mParent.getTotalDragDistance();,
canvas.clipRect(0, -mTop, mScreenWidth, mParent.getTotalDragDistance());
這裡一開始是截取了一個高度為0的矩形,隨著move慢慢變大,後面的draw就只能在這上面操作。重要的就講完了,其他的包括怎麼根據百分比來改變屬性,這些其實可以自己發揮實現自己的效果。
示例程式碼:
mPullToRefreshView = (PullToRefreshView) findViewById(R.id.pull_to_refresh);
mPullToRefreshView.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() {
@Override
public void onRefresh() {
mPullToRefreshView.postDelayed(new Runnable() {
@Override
public void run() {
mPullToRefreshView.setRefreshing(false);
}
}, REFRESH_DELAY);
}
});
相關推薦
android 控制元件 下拉重新整理 phoenix 帶原始碼分析
向納什致敬,鳳凰城永遠的英雄!phoenix Yalantis 致力於提供世界一流的 Android 和 iOS 應用開發服務,因一些 動畫很棒的開源庫為大家所熟知 Phoenix-Android 旨在提供一個簡單的可定製的下拉重新整理功能。 <com
Android SwipeRefreshLayout 官方下拉重新整理控制元件介紹
轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/24521483下面App基本都有下拉重新整理的功能,以前基本都使用XListView或者自己寫一個下拉重新整理,近期Google提供了一個官方的下拉重新整理控制
android自定義下拉重新整理和上拉載入控制元件
import android.content.Context; import android.graphics.Point; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.NestedScro
Android SwipeRefreshLayout官方下拉重新整理控制元件介紹(與知乎Android客戶端下拉重新整理一樣!!)
轉載出處:http://blog.csdn.net/lmj623565791/article/details/24521483 //沒少看鴻洋老師的視訊,一如既往,鴻洋老師講解的淺顯易懂! 下面App基本都有下拉重新整理的功能,以前基本都使用XListView或者自己
Android——(高階控制元件下拉框與搜尋框)
1. 高階控制元件與低階控制元件區別 是否使用介面卡 2. 介面卡種類和作用 2.1 種類 陣列介面卡 ArrayAdapter new ArrayAdapt
Android第六次——(高階控制元件下拉框與搜尋框)
1. 高階控制元件與低階控制元件區別 是否使用介面卡 2. 介面卡種類和作用 2.1 種類 陣列介面卡 ArrayAdapter new ArrayAdapter<String>(this,R.layout.actv_sty
android之官方下拉重新整理元件SwipeRefreshLayout
SwipeRefreshLayout是谷歌官方Sdk提供的下拉元件,繼承自ViewGroup,在support v4相容包下,但必須把你的support library的版本升級到19.1。先看看效果 SwipeRefreshLayout常用API 1、
Android快速開發控制元件---下拉導航選單
LQRDropdownLayout 該專案是下拉導航選單,使用非常簡單。基於FilterDropDownMenu-master專案進行封裝,因為原專案的使用太複雜了,光佈局就得幾十行程式碼,如果專案中多處用到下拉選單,那程式碼的冗餘度就太大了,故本佈局對其進行
Android中ListView下拉重新整理上拉載入更多效果實現
在Android開發中,下拉重新整理和上拉載入更多在很多app中都會有用到,下面就是具體的實現的方法。 首先,我們自定義一個RefreshListView來繼承與ListView,下面是程式碼: package com.example.downrefresh; import
頭部視覺視差(仿QQ控制元件下拉效果)
看到新版QQ空間出來的時候,首頁的下拉的效果比較炫酷,下拉的時候會把整張圖片顯示出來,鬆開手之後就會自己恢復原狀,不說了,上程式碼。 這是效果圖 public class ParallaxListView extends ListView {//關於自定義控制元件//onmeasu
Android簡單的下拉重新整理,上拉載入
先匯入第三方的東西 下載地址 匯入後,就和你的專案聯絡起來 佈局程式碼(activity_pull_to_refresh_action.xml) <?xml version="1.0" encoding="utf-8"?> <Lin
C# combox控制元件下拉列表模糊查詢
//預設查詢全部,可以選擇取消 private void FrmAddShang_Load(object sender, EventArgs e) { SqlConnection conn = DB.lianjie();
C# winfrom Datagridview控制元件下拉選單
拖拽一個datagridview放在介面,編輯列把下來選單那列ColumnType設定成DataGridViewComboBoxColumn 然後在資料一欄的Items可以寫下來選單的內容也可以後臺程式碼寫 下面是後臺程式碼實現功能 private void dgUserAuthData_Edi
Android LRecyclerView實現下拉重新整理,滑動到底部自動載入更多
隨著功能的不斷優化,框架中的類或者介面名字會有變動,為了獲取準確的使用方法,請參考最新的說明文件:點此檢視。簡介LRecyclerView是支援addHeaderView、 addFooterView、下拉重新整理、分頁載入資料的RecyclerView。它對 Rec
自定義控制元件之組合式控制元件 下拉選擇框
自定義控制元件之組合式控制元件 下拉選擇框 文章目錄 自定義控制元件之組合式控制元件 下拉選擇框 零 組合控制元件下載 一 自定義控制元件思路 二 MainActivity核心程式碼 三 activity_main.xml
Android中ListView下拉重新整理的實現
ListView中的下拉重新整理是非常常見的,也是經常使用的,看到有很多同學想要,那我就整理一下,供大家參考。那我就不解釋,直接上程式碼了。 這裡需要自己重寫一下ListView,重寫程式碼如下: package net.loonggg.listview; impor
Android ListView 實現下拉重新整理上拉載入
1.簡介 無疑,在Android開發中,ListView是使用非常頻繁的控制元件之一,ListView提供一個列表的容易,允許我們以列表的形式將資料展示到介面上,但是Google給我們提供的原生ListView的控制元件,雖然在功能上很強大,但是在使用
Android--Google官方下拉重新整理SwipeRefreshLayout(附加增加上拉載入)
Demo_SwipeRefreshLayout 下拉重新整理是用系統的控制元件,但是這控制元件本身不帶有上拉載入,上拉載入需要在ListView的Adapter中設定 需要V4包的支援 compile 'com.android.support:support-v4
jQuery UI Autocomplete控制元件下拉列表固定高度
jQuery ui Autocomplete控制元件下拉列表高度預設是自適應的,有多少資料就會加載出來多少行。以下是給下拉列表設定一個預設高度並新增滾動條的解決辦法。 在樣式裡新增以下程式碼 .ui-autocomplete { max-he
Android控制元件postDelayed用法,View自帶的定時器
有一個需求是這樣的,點選加關注按鈕後,執行關注操作,成功後按鈕文字變為“已關注”,保持3秒,三秒後按鈕文字便問“取消關注”,點選後執行取消關注的操作 可以使用定時器實現,但是使用View的posyDelayed更加方便 原始碼如下: android.view.View