自定義 behavior - 完美仿 QQ 瀏覽器首頁,美團商家詳情頁
使用CoordinatorLayout打造各種炫酷的效果
自定義Behavior —— 仿知乎,FloatActionButton隱藏與展示
NestedScrolling 機制深入解析
一步步帶你讀懂 CoordinatorLayout 原始碼
自定義 Behavior -仿新浪微博發現頁的實現
ViewPager,ScrollView 巢狀ViewPager滑動衝突解決
自定義 behavior - 完美仿 QQ 瀏覽器首頁,美團商家詳情頁
前言
記得兩年前的時候,曾寫過自定義 behavior 的文章 自定義 Behavior -仿新浪微博發現頁的實現,到現在差不多有一萬多的閱讀量吧。
今天,對該 behavior 進行升級,相對於兩年前的 behavior,增加了以下功能
- 級聯滑動過程中增加監聽回撥,方便外部根據滑動距離,進行相應的動畫,展現炫酷的 UI,通過 setPagerStateListener 設定回撥監聽
- 在滑動到頂部的時候,可以設定是否能夠滑動將 Head 滑動下來,方法為 setCouldScroollOpen
- 手指在 header 部分慣性滑動的時候,增加 fling 回撥,可根據需要,是否滑動 content 部分的 list ,方法為 setOnHeaderFlingListener
- HeaderBehavior ,ContentBehavior 程式碼優化,與業務邏輯剝離開,方便複用。
使用說明
效果圖
我們先來看一下新浪微博發現頁的效果:
接下來我們在來看一下我們兩年前仿照新浪微博實現的效果
仿 QQ 瀏覽器
仿美團商家詳情頁面的:
分析說明:
有兩種狀態,open 和 close 狀態。
- open 狀態指 Tab+ViewPager 還沒有滑動到頂部的時候,header 還 沒有被完全移除螢幕的時候
- close 狀態指 Tab+ViewPager 滑動到頂部的時候,Header 被移除螢幕的時候
從效果圖,我們可以看到 在 open 狀態下,我們向上滑動 ViewPager 裡面的 RecyclerView 的 時候,RecyclerView 並不會向上移動(RecyclerView 的滑動事件交給 外部的容器處理,被被全部消費掉了),而是整個佈局(指 Header + Tab +ViewPager)會向上偏移。當 Tab 滑動到頂部的時候,我們向上滑動 ViewPager 裡面的 RecyclerView 的時候,RecyclerView 可以正常向上滑動,即此時外部容器沒有攔截滑動事件。
同時我們可以看到在 open 狀態的時候,我們是不支援下拉重新整理的,這個比較容易實現,監聽頁面的狀態,如果是 open 狀態,我們設定 SwipeRefreshLayout setEnabled 為 false,這樣不會 攔截事件,在頁面 close 的時候,設定 SwipeRefreshLayout setEnabled 為 TRUE,這樣就可以支援下拉重新整理了。
基於上面的分析,我們這裡可以把整個效果劃分為三個部分
第一部分 Header 部分:在 Header 部分還沒有滑動到頂部的時候(即 open 的時候),跟隨手指滑動
第二部分 Content 部分:我們向上滑動的時候,當Header 處於 open 狀態,這時候 Header 向上滑動, content 部分的 recyclerView 不會滑動,當 header 處於 close 狀態,content 部分向上滑動, RecyclerView 向上滑動。當我們向下滑動的時候,header 並不會隨著滑動,只會滑動 content 部分的 recyclerView
第三部分 search 部分:當我們向上滑動的時候,Search 部分會隨著滑動,最終停留在固定的位置.
我們把這三部分的關係定義為 Content 依賴於 Header。Header 移動的時候,Content 跟著 移動。所以,我們在處理滑動事件的時候,只需要處理好 Header 部分的 Behavior 就oK了,Content 部分的 Behavior 不需要處理滑動事件,只需依賴於 Header ,跟著做相應的移動即可。Search 部分的 behavior 也不需要處理滑動事件,只需依賴與 Header,跟著做相應的移動。
至於具體怎麼實現的,可以看自定義 Behavior -仿新浪微博發現頁的實現,核心思想差不多,這裡不再重複。
使用說明
這裡我們已仿 QQ 瀏覽器 demo 進行說明:
我們一起來看一下怎樣使用:簡單來說,只需要兩步:
- 第一步,分別在 xml 檔案中,為 header 部分, content 部分指定我們對應的 behavior
- 第二部分,在程式碼裡面設定一些配置引數
第一步:編寫 xml 檔案,並指定相應的 behavior
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:fitsSystemWindows="true">
<!-- Header 部分-->
<FrameLayout
android:id="@+id/id_uc_news_header_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/behavior_qq_browser_header_pager">
<com.xj.qqbroswer.behavior.base.NestedLinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/header_height"
android:orientation="vertical">
<TextView
android:id="@+id/news_tv_header_pager"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center"
android:text="QQBrowser Header"
android:textColor="@android:color/white" />
</com.xj.qqbroswer.behavior.base.NestedLinearLayout>
</FrameLayout>
<!-- ContentProvide 部分-->
<LinearLayout
android:id="@+id/behavior_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_behavior="@string/behavior_contents">
<android.support.design.widget.TabLayout
android:id="@+id/id_uc_news_tab"
android:layout_width="match_parent"
android:layout_height="@dimen/tabs_height"
android:background="@color/colorPrimary"
app:tabGravity="fill"
app:tabIndicatorColor="@color/colorPrimaryLight"
app:tabSelectedTextColor="@color/colorPrimaryLight"
app:tabTextColor="@color/colorPrimaryIcons" />
<android.support.v4.view.ViewPager
android:id="@+id/id_uc_news_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F0F4C3">
</android.support.v4.view.ViewPager>
</LinearLayout>
<!--search 部分-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/header_title_height"
app:layout_behavior="@string/behavior_search">
<android.support.v7.widget.SearchView
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="50dp"
android:background="@android:color/white"
app:defaultQueryHint="搜尋"
app:queryHint="搜尋">
</android.support.v7.widget.SearchView>
<android.support.v7.widget.AppCompatImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:src="@mipmap/camera"
android:tint="@android:color/white" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
第二步:在程式碼裡面動態設定一些引數
private void initBehavior() {
Resources resources = DemoApplication.getAppContext().getResources();
mHeaderBehavior = (QQBrowserHeaderBehavior) ((CoordinatorLayout.LayoutParams) findViewById(R.id.id_uc_news_header_pager).getLayoutParams()).getBehavior();
mHeaderBehavior.setPagerStateListener(new QQBrowserHeaderBehavior.OnPagerStateListener() {
@Override
public void onPagerClosed() {
if (BuildConfig.DEBUG) {
Log.d(TAG, "onPagerClosed: ");
}
Snackbar.make(mNewsPager, "pager closed", Snackbar.LENGTH_SHORT).show();
setFragmentRefreshEnabled(true);
setViewPagerScrollEnable(mNewsPager, true);
}
@Override
public void onScrollChange(boolean isUp, int dy, int type) {
}
@Override
public void onPagerOpened() {
Snackbar.make(mNewsPager, "pager opened", Snackbar.LENGTH_SHORT).show();
setFragmentRefreshEnabled(false);
}
});
// 設定為 header height 的相反數
mHeaderBehavior.setHeaderOffsetRange(-resources.getDimensionPixelOffset(R.dimen.header_height));
// 設定 header close 的時候是否能夠通過滑動開啟
mHeaderBehavior.setCouldScroollOpen(false);
mContentBehavior = (QQBrowserContentBehavior) ((CoordinatorLayout.LayoutParams) findViewById(R.id.behavior_content).getLayoutParams()).getBehavior();
// 設定依賴於哪一個 id,這裡要設定為 Header layout id
mContentBehavior.setDependsLayoutId(R.id.id_uc_news_header_pager);
// 設定 content 部分最終停留的位置
mContentBehavior.setFinalY(resources.getDimensionPixelOffset(R.dimen.header_title_height));
}
mHeaderBehavior.setHeaderOffsetRange 設定 Header 部分的偏移量,我們是通過 translationY 實現的,因此我們一般設定為 header 高度的相反數即可。
mHeaderBehavior.setCouldScroollOpen(false) , 設定 header close 的時候是否能夠通過滑動開啟。
mContentBehavior.setDependsLayoutId(R.id.id_uc_news_header_pager);設定依賴於哪一個 id,這裡要設定為 Header layout id。 mContentBehavior.setFinalY 設定 content 部分最終停留的位置。
我們來看一下 OnPagerStateListener 的回撥
/**
* callback for HeaderPager 's state
*/
public interface OnPagerStateListener {
/**
* do callback when pager closed
*/
void onPagerClosed();
/**
* when scrooll, it would call back
*
* @param isUp isScroollUp
* @param dy child.getTanslationY
* @param type touch or not touch, TYPE_TOUCH, TYPE_NON_TOUCH
*/
void onScrollChange(boolean isUp, int dy, @ViewCompat.NestedScrollType int type);
/**
* do callback when pager opened
*/
void onPagerOpened();
}
主要有三個方法,第一個方法,onPagerClosed 當 header close 的時候,會回撥,第二個方法,當 header 滑動距離變化的時候,會回撥 onScrollChange 方法。它有三個引數, isUp 代表是否是向上滑動, dy 代表 header 的偏移量, type 代表型別是 touch 或者是非 touch 的(即 fling 滑動的)
如果你想要做一些酷炫的效果的話,你可以在 onScrollChange 方法中,根據滑動的距離,各個不同的 View 做相應的動畫。
仿美圖商家詳情頁面
步驟跟上面的仿 QQ 瀏覽器的步驟是一樣的,這裡不再重複相同的步驟,說幾個關鍵點:
第一:在頁面 header close 的時候,我們可以通過滑動開啟header,這是通過呼叫 mHeaderBehavior.setCouldScroollOpen(true); 實現的。
第二:滑動 header, fling 的時候,可以看到 content 部分的 recyclerView 也在滑動,我們是通過 header 的 fling 事件做到的,在 onFlingStart 的時候手動呼叫 RecyclerView 的 smoothScrollBy 進行滑動。
mHeaderBehavior.setOnHeaderFlingListener(new HeaderFlingRunnable.OnHeaderFlingListener() {
@Override
public void onFlingFinish() {
}
@Override
public void onFlingStart(View child, View target, float velocityX, float velocityY) {
Log.i(TAG, "onFlingStart: velocityY =" + velocityY);
if (velocityY < 0) {
mRecyclerView.smoothScrollBy(0, (int) Math.abs(velocityY), new AccelerateDecelerateInterpolator());
}
}
@Override
public void onHeaderClose() {
}
@Override
public void onHeaderOpen() {
}
});
碰到的坑
header 部分無法響應滑動事件
我們是通過自定義一個 NestedLinearLayout ,重寫它的 onTouchEvent 事件,通過 NestedScrolling 機制將事件傳遞給 NestedScrollingParent,即 CoordinatorLayout,而 NestedScrollingParent 會交給子 View 的 behavior 進行處理。
@Override
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN:
startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL
| ViewCompat.SCROLL_AXIS_VERTICAL);
break;
case MotionEvent.ACTION_MOVE:
int dy = (int) (event.getRawY() - lastY);
lastY = (int) event.getRawY();
// dy < 0 上滑, dy>0 下拉
if (dy < 0) { // 上滑的時候交給父類去處理
if (startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) // 如果找到了支援巢狀滾動的父類
&& dispatchNestedPreScroll(0, -dy, consumed, offset)) {//
// 父類進行了一部分滾動
}
} else {
if (startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) // 如果找到了支援巢狀滾動的父類
&& dispatchNestedScroll(0, 0, 0, -dy, offset)) {//
// 父類進行了一部分滾動
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
stopNestedScroll();
break;
}
return super.onTouchEvent(event);
}
當我們給 header 的子 View 設定點選事件的時候,無法滑動 header
對 Android 事件分發機制有一定了解的,都知道,在 Android 中,預設的事件傳遞機制是這樣的,
當TouchEvent發生時,首先Activity將TouchEvent傳遞給最頂層的View,TouchEvent最先到達最頂層 view 的 dispatchTouchEvent ,然後由 dispatchTouchEvent 方法進行分發。
- 如果dispatchTouchEvent返回true 消費事件,事件終結。
如果dispatchTouchEvent返回 false ,則回傳給父View的onTouchEvent事件處理;
onTouchEvent事件返回true,事件終結,返回false,交給父View的OnTouchEvent方法處理
如果dispatchTouchEvent返回super的話,預設會呼叫自己的onInterceptTouchEvent方法
預設的情況下interceptTouchEvent回撥用super方法,super方法預設返回false,所以會交給子View的onDispatchTouchEvent方法處理
如果 interceptTouchEvent 返回 true ,也就是攔截掉了,則交給它的 onTouchEvent 來處理
如果 interceptTouchEvent 返回 false ,那麼就傳遞給子 view ,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發。
因此,當我們給子 View 設定點選事件的時候,由於預設的 parent 沒有攔截事件,會走到子 View 的 onToucheEvent 事件中,由於設定了點選事件,事件被消費了,所以不會回撥父 View onTouchEvent 中的 ACTION_MOVE 事件。
解決辦法: 重寫 NestedLinearLayout 的 onInterceptToucheEvent 事件,當是 ACTION_MOVE 事件的時候,返回 true ,攔截,這樣會呼叫自己的 onTouchEvent 事件,從而保證可以滑動。
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = (int) event.getRawY();
// 當開始滑動的時候,告訴父view
startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL
| ViewCompat.SCROLL_AXIS_VERTICAL);
break;
case MotionEvent.ACTION_MOVE:
// 確保不消耗 ACTION_DOWN 事件
if (Math.abs(event.getRawY() - mDownY) > mScaledTouchSlop) {
logD("onInterceptTouchEvent: ACTION_MOVE mScaledTouchSlop =" + mScaledTouchSlop);
return true;
}
}
return super.onInterceptTouchEvent(event);
}
但這裡還有一個坑,正常一個點選事件,會促發 ACTION_DOWN, ACTION_MOVE, ACTION_UP,如果我們直接在 ACTION_MOVE 裡面返回 true,將會導致子 View 的 onClick 事件失效。
解決辦法:
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mScaledTouchSlop = configuration.getScaledTouchSlop();
if (Math.abs(event.getRawY() - mDownY) > mScaledTouchSlop) {
return true;
}
關於滑動衝突解決的,可以看我以前的一篇部落格:ViewPager,ScrollView 巢狀ViewPager滑動衝突解決
如何判斷 header 是 fling 動作
我們這裡通過手勢處理器 GestureDetector 做到的,當然你也可以通過 VelocityTracker 計算,只不過比較繁瑣
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
}
GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
-----// 省略若干程式碼
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d(TAG, "onFling: velocityY =" + velocityY);
// fling((int) velocityY);
getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
return false;
}
};
mGestureDetector = new GestureDetector(getContext(), onGestureListener);
題外話
有時候,做一些筆記真的挺重要的。
這一次寫這一篇部落格,是因為在專案中要做類似的效果。剛開始,真的沒什麼思路。但清楚得記得兩年前寫過類似的文章,具體實現原理早已忘光。我查看了兩年前的部落格,整理了一下思路,將程式碼搬到專案中,發現了一些坑。修修補補,把坑都填了。
試想一下,如果當初沒有將原理記錄下來,這個效果,真的挺難實現的。如果你對 Coordinatorlayout , behavior,NestedScroll 機制這些不熟悉,你根本就無法實現。兩年前寫 自定義 Behavior -仿新浪微博發現頁的實現 這篇部落格的時候,收到挺多私信的,有一些反饋說他們做這個效果做了兩個多星期還是無法實現,挺感謝我寫這篇部落格的。因此,從現在起,不妨嘗試一下多做一下筆記。真的,好記性不如爛筆頭。
第二點感觸比較深的是,剛開始,我看了我兩年前寫的程式碼,我一開始的反應,我去,這是什麼垃圾程式碼。確實,很多地方寫得挺爛的,behavior 耦合業務邏輯,很難複用,也不好維護。因此,這一次,我在空閒的時間將 behavior 抽離出來,以後要實現類似的效果,輕鬆實現, biu biu biu。
說這麼多,總結如下
- 遇到不會的多做筆記,尤其是涉及到原理的
- 對程式碼要有敬畏之心,不多說,自己領悟取
- 保持一顆謙卑之心
CoordinatorLayoutExample
覺得效果還不錯的,可以動手掃一掃關注我的微信公眾號,或者到我的 github 上面 star,謝謝
相關推薦
自定義 behavior - 完美仿 QQ 瀏覽器首頁,美團商家詳情頁
使用CoordinatorLayout打造各種炫酷的效果 自定義Behavior —— 仿知乎,FloatActionButton隱藏與展示 NestedScrolling 機制深入解析 一步步帶你讀懂 CoordinatorLayout 原始碼 自定義 Behavior -仿新浪微博發現頁的實現 ViewP
Android自定義View之仿QQ側滑選單實現
最近,由於正在做的一個應用中要用到側滑選單,所以通過查資料看視訊,學習了一下自定義View,實現一個類似於QQ的側滑選單,順便還將其封裝為自定義元件,可以實現類似QQ的側滑選單和抽屜式側滑選單兩種選單。 下面先放上效果圖: 我們這裡的側
【Android】自定義控制元件-仿QQ聯絡人側滑條目,右側滑選單。
一直沒有寫部落格的習慣,一直都是看別人的部落格,學習別人的東西。平時工作中總會遇到或大或小的問題,往往是上百度CSDN查詢答案。今天嘗試著寫部落格,一是更加深入地熟悉一下部落格;二是轉變一下學習方式;三是把自己所學的東西分享出來,幫助別人的同時也提升了自己!
Android自定義控制元件-仿淘寶ios客戶端天貓商品詳情介面動效
效果圖 原始碼和例子 效果描述 一個自定義控制元件繼承自ScrollView,下拉時header會放大鬆開後會恢復原狀,上滑時header會被下面的內容吃掉蓋住而且會稍稍往上滑,在header高度範圍內滑動時導航欄背景和導航欄的按鈕會反向改變透明度形成一種對比
CoordinatorLayout自定義behavior(仿20CM動畫效果)
首先上一張效果圖: 動畫分為三部分:1.視差效果:進入頁面時,頭圖和列表同時向下移動,且列表有一個從0到1的alpha漸變。 2.向上推動列表到距離頂部的距離為0dp ~ 96dp(即頭部有返回關注按鈕的導航欄高度的2倍,此處可根據需要自己定義),導航欄會有一個顏
UC瀏覽器主介面滑動摺疊效果 使用自定義behavior實現 難度五顆星*****
RcycleView上的HeadScrollBehavior 思路: 1。讓recycleview居於頭部的下方 ---方案:重寫layoutDependsOn 讓當前recycleview去依賴頭部檢視重寫onDependentViewChanged 獲取到依賴的頭部檢視的高度,給recycleview設
CoordinateLayout 自定義Behavior 仿百度外賣效果 實踐
高仿百度外賣效果 程式碼地址:https://github.com/yaodiwei/CoordinatorLayoutTester 真百度外賣效果 自定義Behavior一半分為兩種型別 一種是重寫layoutDependsOn和onDepend
dedecms自定義模型之獨立模型在首頁、列表頁、內容調用內容
兩個 定義 blog typeid 註意 lists lis curl lds dedecms關於自定義模型(獨立模型)的首頁、列表頁、內容怎麽調用?在後臺自定義模型(獨立模型)的建立及自定義字段的添加比較簡單,需要註意兩點: (1)如果某個字段需要在前臺列表頁顯示,則在前
Android bc信用盤搭建自定義behavior 實現上滑 隱藏底部view
退出 Y軸 log rect app sum string dsl oss 布局 <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent"
Android筆記--自定義控件仿遙控器的圓形上下左右OK圓盤按鈕
分享圖片 https click cas tween .com show idc rotate 原文:Android筆記--自定義控件仿遙控器的圓形上下左右OK圓盤按鈕 上
[uwp]自定義Behavior之隨意拖動
原文: [uwp]自定義Behavior之隨意拖動 由於最近有需求,所以自定義了一個隨意拖動元素的Behavior. 當然在使用這個自定義的Behavior時,有個小假設:拖動元素必須是Canvas容器的子元素。 實現原理比較簡單低效: 監聽被拖動元素的PointerMoved事件,當
android自定義View之仿通訊錄側邊欄滑動,實現A-Z字母檢索
我們的手機通訊錄一般都有這樣的效果,如下圖: OK,這種效果大家都見得多了,基本上所有的android手機通訊錄都有這樣的效果。那我們今天就來看看這個效果該怎麼實現。 一.概述 1.頁面功能分析 整體上來說,左邊是一個ListView,右邊是一個自定義View,但
小程式自定義數字鍵盤|仿微信支付、支付寶支付密碼鍵盤
微信小程式自定義鍵盤外掛wcKeyboard|仿微信數字軟鍵盤|仿支付寶自定義數字鍵盤|小程式自定義模擬系統鍵盤 前段時間有開發過一個html5仿支付寶、微信支付數字鍵盤,在某些情況下自定義數字鍵盤應用還是蠻多的,比如 購物商城系統 零錢付款 ,會員卡支付,恰好微信小程式沒有內部數字鍵盤元件,這樣輸入密
自定義View之仿ios分段選擇器
一:效果 1.1 可動態新增或刪除tab,更改指定tab的文字。 二:實現思路 自定義view,實現效果 動態建立textview,有幾個tab建立幾個textview 第一個tab和最後一個tab為圓角矩形,其餘的為直角矩形,通過shape檔案完成 預設選
CoordinatorLayout的使用(二)——自定義Behavior
我們在上一篇文章CoordinatorLayout的使用(一)——簡單使用中介紹了CoordinatorLayout的基本用法。為什麼CoordinatorLayout能夠這麼方便的幫助我們非常簡單的就實現炫酷的UI互動效果呢?這就不得不提到它的內部類Behavior了。其實Coor
CoordinatorLayout高階用法-自定義Behavior
在新的support design中,CoordinatorLayout可以說是最重要的一個控制元件了,CoordinatorLayout給我們帶來了一種新的事件的處理方式——behavior,你是不是還記得我們在使用CoordinatorLayout的時候,一
Android 轉載---自定義PopWindow 類似於QQ右上角的效果
前段時間在個人開發的專案中需要用到彈出選單,類似QQ右上角的彈出選單,自己使用popwin的次數也不是很多,其中也遇到過一點問題,今天正好有時間就把一些經驗分享給大家。 先來看看最終實現過後的效果怎麼樣,下面放上圖 自定義的彈出選單是繼承的popwin,並不是view 因為
Android 自定義Behavior
一、Behavior的介紹 1、什麼是Behavior 上篇文章我們講到CoordinatorLayout 配合AppBarLayout、CollapsingToolbarLayout實現了Toolbar的隱藏和摺疊,但他們之間能夠進行互動,其實就是通過
自定義控制元件:QQ氣泡效果粘性控制元件的實現
學習目的 瞭解幾何圖形工具的用法 掌握畫不規則圖形的方法 應用場景:未讀提醒,效果圖: 繪製一幀的效果 畫一幀粘性控制元件的步驟分析 畫一個固定圓 畫一個拖拽圓 畫中間連線部分 將中間連線部分水平放置,四個角的座標定為固定值,
Android自定義控制元件——仿微信半透明載入框
在我們的app中不免回遇到耗時操作,這時我們給個載入框進行過度,使使用者體驗大大加強。 一、CustomDialog類的編寫 package com.daqsoft.jingguan.weight; import android.app.Dialo