Android實現(xiàn)京東上滑效果
本文實例為大家分享了Android實現(xiàn)京東上滑效果的具體代碼,供大家參考,具體內(nèi)容如下
前言:
現(xiàn)在很多app首頁的結(jié)構(gòu)都有頭部廣告,上滑固定toolbar及側(cè)滑廣告位等展示,典型的比如招商銀行app,支付寶、哈羅單車、京東、蘇寧金融也有類似的效果。具體如下,左側(cè)為有廣告位存在的情況,右側(cè)無頂部廣告位的樣式:
效果說明:頭部廣告一般在節(jié)假日有活動的時候展示,頁面上滑會有固定標(biāo)題欄展示,靠底部右側(cè)有一個小的廣告位,滑動主屏幕時,廣告位會向右側(cè)收起,屏幕不滾動時,廣告位顯示。本文旨在為實現(xiàn)這種效果提供一種方式,歡迎有其他想法的小伙伴評論交流,接下來將分三塊分別實現(xiàn)頂部廣告位,滑動固定toolbar及側(cè)滑廣告位效果。
頂部廣告位:
分析:有廣告位圖片和沒廣告位圖片的區(qū)別在于下方白色內(nèi)容區(qū)域在圖片下方還是在頂部4個功能項的下方,一種方式:根據(jù)是否有廣告位動態(tài)調(diào)整margin高度;方式二:使用約束布局,根據(jù)情況改變白色區(qū)域內(nèi)容相對誰來布局
實現(xiàn):
這里我們使用第二種方式實現(xiàn):
外層父布局為ConstraintLayout,里面內(nèi)容包含三部分:
1. 底部背景ImageView控件A,有圖片時設(shè)置src,無圖時設(shè)置background;
2. 四個功能選項部分B,即無圖時白色區(qū)域的約束對象;
3. 白色內(nèi)容區(qū)域C,無圖時約束對象為B,有圖時約束對象為A;
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/cl_header" android:layout_width="match_parent" android:layout_height="wrap_content"> ? <ImageView ? ? android:id="@+id/iv_bg_header" ? ? android:layout_width="match_parent" ? ? android:layout_height="300dp" ? ? android:background="@drawable/shape_gradient_yellow" ? ? android:contentDescription="@null" ? ? android:scaleType="fitXY" ? ? app:layout_constraintStart_toStartOf="parent" ? ? app:layout_constraintTop_toTopOf="parent" /> ? <LinearLayout ? ? android:id="@+id/ll_search" ? ? android:layout_width="match_parent" ? ? android:layout_height="wrap_content" ? ? android:layout_marginStart="12dp" ? ? android:layout_marginTop="50dp" ? ? android:layout_marginEnd="12dp" ? ? android:clickable="true" ? ? android:gravity="center_vertical" ? ? android:orientation="horizontal" ? ? app:layout_constraintStart_toStartOf="parent" ? ? app:layout_constraintTop_toTopOf="parent"> ? ? ? <EditText ? ? ? ? android:layout_width="0dp" ? ? ? ? android:layout_height="32dp" ? ? ? ? android:layout_weight="1" ? ? ? ? android:background="@drawable/shape_edit_text_stroke_white" ? ? ? ? android:drawableStart="@drawable/vector_search" ? ? ? ? android:drawablePadding="4dp" ? ? ? ? android:drawableTint="@color/color_icon" ? ? ? ? android:hint="請輸入內(nèi)容" ? ? ? ? android:importantForAutofill="no" ? ? ? ? android:paddingStart="8dp" ? ? ? ? android:textColorHint="@color/color_icon" ? ? ? ? android:textSize="14sp" ? ? ? ? tools:ignore="TextFields" /> ? ? ? <ImageView ? ? ? ? android:layout_width="wrap_content" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:layout_marginStart="12dp" ? ? ? ? android:contentDescription="@null" ? ? ? ? android:src="@drawable/vector_custom_service" ? ? ? ? android:tint="@color/color_icon" /> ? ? ? <ImageView ? ? ? ? android:layout_width="wrap_content" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:layout_marginStart="12dp" ? ? ? ? android:contentDescription="@null" ? ? ? ? android:src="@drawable/vector_message" ? ? ? ? android:tint="@color/color_icon" /> ? </LinearLayout> ? <LinearLayout ? ? android:id="@+id/ll_header" ? ? android:layout_width="match_parent" ? ? android:layout_height="wrap_content" ? ? android:clickable="true" ? ? android:orientation="horizontal" ? ? android:paddingTop="20dp" ? ? app:layout_constraintStart_toStartOf="parent" ? ? app:layout_constraintTop_toBottomOf="@id/ll_search"> ? ? ? <TextView ? ? ? ? android:layout_width="0dp" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:layout_weight="1" ? ? ? ? android:drawableTop="@drawable/vector_code" ? ? ? ? android:drawablePadding="8dp" ? ? ? ? android:gravity="center_horizontal" ? ? ? ? android:text="收/付款" ? ? ? ? android:textColor="@color/color_white" /> ? ? ? <TextView ? ? ? ? android:layout_width="0dp" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:layout_weight="1" ? ? ? ? android:drawableTop="@drawable/vector_shopping" ? ? ? ? android:drawablePadding="8dp" ? ? ? ? android:gravity="center_horizontal" ? ? ? ? android:text="購物" ? ? ? ? android:textColor="@color/color_white" /> ? ? ? <TextView ? ? ? ? android:layout_width="0dp" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:layout_weight="1" ? ? ? ? android:drawableTop="@drawable/vector_setting" ? ? ? ? android:drawablePadding="8dp" ? ? ? ? android:gravity="center_horizontal" ? ? ? ? android:text="設(shè)置" ? ? ? ? android:textColor="@color/color_white" /> ? ? ? <TextView ? ? ? ? android:layout_width="0dp" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:layout_weight="1" ? ? ? ? android:drawableTop="@drawable/vector_function" ? ? ? ? android:drawablePadding="8dp" ? ? ? ? android:gravity="center_horizontal" ? ? ? ? android:text="全部功能" ? ? ? ? android:textColor="@color/color_white" /> ? </LinearLayout> ? <include ? ? android:id="@+id/ll_services" ? ? layout="@layout/layout_services" ? ? android:layout_width="match_parent" ? ? android:layout_height="wrap_content" ? ? android:layout_marginTop="20dp" ? ? app:layout_constraintStart_toStartOf="parent" ? ? app:layout_constraintTop_toBottomOf="@id/ll_header" /> ? </androidx.constraintlayout.widget.ConstraintLayout>
展示廣告位的情況下
ivBgHeader.setBackgroundResource(0); ivBgHeader.setImageResource(R.mipmap.ic_ad_banner); ivBgHeader.setOnClickListener(v -> Toast.makeText(getActivity(), "拍一拍", Toast.LENGTH_SHORT).show()); ConstraintSet c = new ConstraintSet(); c.clone(clHeader); c.connect(llServices.getId(), ConstraintSet.TOP, ivBgHeader.getId(), ConstraintSet.BOTTOM, Utils.dip2pixel(getActivity(), 20)); c.applyTo(clHeader);
不展示廣告位情況下:
ivBgHeader.setBackgroundResource(R.drawable.shape_gradient_yellow); ivBgHeader.setImageResource(0); ivBgHeader.setOnClickListener(null); ConstraintSet c = new ConstraintSet(); c.clone(clHeader); c.connect(llServices.getId(), ConstraintSet.TOP, llHeader.getId(), ConstraintSet.BOTTOM, Utils.dip2pixel(getActivity(), 20)); c.applyTo(clHeader);
側(cè)滑廣告位:
分析:首頁的滑動布局為NestedScrollView控件實現(xiàn)的,NestedScrollView并沒有像RecyclerView一樣提供滑動狀態(tài)的監(jiān)聽,所以關(guān)于滑動狀態(tài)需要我們?nèi)ヅ袛啵鼫?zhǔn)確可以說是監(jiān)聽狀態(tài)的改變結(jié)果,而不是監(jiān)聽狀態(tài)的過程,狀態(tài)結(jié)果分兩種:靜止和滑動。NestedScrollView提供了onScrollChanged回調(diào)方法,在內(nèi)部View滑動時會走到這個回調(diào),所以走這個回調(diào)肯定是滑動中狀態(tài),如何判斷靜止?fàn)顟B(tài)呢?通常是使用定時器或是handler發(fā)送消息的方式去監(jiān)聽。
實現(xiàn):
自定義View繼承NestedScrollView,在內(nèi)部將狀態(tài)提供給外部去使用
public class ObservableScrollView extends NestedScrollView { ? ? ? public static final int STATE_IDLE = 1; ? ? public static final int STATE_SCROLL = 2; ? ? ? public int currentState = STATE_IDLE; ? ? private boolean isOnActionDown = false; ? ? ? private ScrollStateChangeListener scrollStateChangeListener; ? ? private Handler mHandler; ? ? private static final int MSG_IS_SCROLL = 1; ? ? ? public ObservableScrollView(@NonNull Context context) { ? ? ? ? this(context, null); ? ? } ? ? ? public ObservableScrollView(@NonNull Context context, @Nullable AttributeSet attrs) { ? ? ? ? this(context, attrs, 0); ? ? } ? ? ? public ObservableScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { ? ? ? ? super(context, attrs, defStyleAttr); ? ? ? ? mHandler = new Handler(Looper.getMainLooper()) { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void handleMessage(@NonNull Message msg) { ? ? ? ? ? ? ? ? super.handleMessage(msg); ? ? ? ? ? ? ? ? if (currentState == STATE_SCROLL) { ? ? ? ? ? ? ? ? ? ? currentState = STATE_IDLE; ? ? ? ? ? ? ? ? ? ? if (null != scrollStateChangeListener) { ? ? ? ? ? ? ? ? ? ? ? ? scrollStateChangeListener.onScrollChange(ObservableScrollView.this, currentState); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? }; ? ? } ? ? ? @Override ? ? protected void onScrollChanged(int x, int y, int oldX, int oldY) { ? ? ? ? super.onScrollChanged(x, y, oldX, oldY); ? ? ? ? if (isOnActionDown) ? ? ? ? ? ? return; ? ? ? ? mHandler.removeCallbacksAndMessages(null); ? ? ? ? mHandler.sendEmptyMessageDelayed(MSG_IS_SCROLL, 500); ? ? ? ? setStatus(); ? ? } ? ? ? private void setStatus() { ? ? ? ? if (currentState == STATE_IDLE) { ? ? ? ? ? ? currentState = STATE_SCROLL; ? ? ? ? ? ? if (null != scrollStateChangeListener) { ? ? ? ? ? ? ? ? scrollStateChangeListener.onScrollChange(this, currentState); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? ? @Override ? ? public boolean onTouchEvent(MotionEvent ev) { ? ? ? ? switch (ev.getAction()) { ? ? ? ? ? ? case MotionEvent.ACTION_UP: ? ? ? ? ? ? case MotionEvent.ACTION_CANCEL: ? ? ? ? ? ? ? ? isOnActionDown = false; ? ? ? ? ? ? ? ? mHandler.sendEmptyMessageDelayed(MSG_IS_SCROLL, 500); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? case MotionEvent.ACTION_DOWN: ? ? ? ? ? ? case MotionEvent.ACTION_MOVE: ? ? ? ? ? ? ? ? mHandler.removeCallbacksAndMessages(null); ? ? ? ? ? ? ? ? setStatus(); ? ? ? ? ? ? ? ? isOnActionDown = true; ? ? ? ? ? ? ? ? break; ? ? ? ? } ? ? ? ? return super.onTouchEvent(ev); ? ? } ? ? ? public void addOnScrollChangeListener(ScrollStateChangeListener scrollStateChangeListener) { ? ? ? ? this.scrollStateChangeListener = scrollStateChangeListener; ? ? } ? ? ? public interface ScrollStateChangeListener { ? ? ? ? void onScrollChange(ObservableScrollView view, int newState); ? ? } ? ? ? public void onDestroy() { ? ? ? ? if (null != mHandler) { ? ? ? ? ? ? mHandler.removeCallbacksAndMessages(null); ? ? ? ? } ? ? } }
外部使用:
scrollView.addOnScrollChangeListener(new ObservableScrollView.ScrollStateChangeListener() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onScrollChange(ObservableScrollView view, int newState) { ? ? ? ? ? ? ? ? if (newState == STATE_IDLE) { ? ? ? ? ? ? ? ? ? ? ivSideScroll.animate().translationX(0); ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? ivSideScroll.animate().translationX(200); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? });
固定頂部效果:
分析:監(jiān)聽scrollView滑動變化量,分別在上滑和下滑時做固定內(nèi)容部分的透明度值變化
實現(xiàn):
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { ? ? ? ? ? ? ? ? // 向上滑動,超過100像素則透明度開始由0 -> 1? ? ? ? ? ? ? ? ? if (scrollY - oldScrollY > 0) { ? ? ? ? ? ? ? ? ? ? float alpha = Math.min(1, (scrollY - 100) / 50f); ? ? ? ? ? ? ? ? ? ? llFixedHeader.setAlpha(alpha); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? // 向下滑動,小于100像素則透明度開始 1 -> 0 ? ? ? ? ? ? ? ? if (scrollY - oldScrollY < 0) { ? ? ? ? ? ? ? ? ? ? float alpha = Math.max(0, (scrollY - 100) / 50f); ? ? ? ? ? ? ? ? ? ? llFixedHeader.setAlpha(alpha); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? });
最終實現(xiàn)效果:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android工具類ImgUtil選擇相機和系統(tǒng)相冊
這篇文章主要為大家詳細(xì)介紹了Android工具類ImgUtil選擇相機和系統(tǒng)相冊,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10flutter自定義InheritedProvider實現(xiàn)狀態(tài)管理詳解
這篇文章主要為大家介紹了flutter自定義InheritedProvider實現(xiàn)狀態(tài)管理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Android編程開發(fā)之在Canvas中利用Path繪制基本圖形(圓形,矩形,橢圓,三角形等)
這篇文章主要介紹了Android編程開發(fā)之在Canvas中利用Path繪制基本圖形的方法,涉及Android基本的圖形繪制技巧,結(jié)合實例分析了繪制圓形,矩形,橢圓,三角形等基本圖形的實現(xiàn)方法,需要的朋友可以參考下2016-01-01Android自定義VIew實現(xiàn)衛(wèi)星菜單效果淺析
這篇文章主要介紹了Android自定義VIew實現(xiàn)衛(wèi)星菜單效果淺析,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-11-11Android ViewPager實現(xiàn)選項卡切換
這篇文章主要介紹了Android ViewPager實現(xiàn)選項卡切換,詳細(xì)分析了ViewPager實現(xiàn)選項卡切換功能,感興趣的小伙伴們可以參考一下2016-02-02一文了解Android?ViewModelScope?如何自動取消協(xié)程
這篇文章主要介紹了一文了解Android?ViewModelScope?如何自動取消協(xié)程,文章圍繞主題站展開詳細(xì)的內(nèi)容介紹,具有一定參考價值,感興趣的小伙伴可以參考一下2022-07-07