效果展示

背景
對于這個效果,我之前寫過一種實作方法 仿QQ6.0主頁面側滑效果,這種方法跟手性不好,如果復現了的朋友應該能體會到,并且,還得自己處理很多 onTouch 事件,所以,這篇文章帶大家體驗一下另外一種實作方法,繼承自 HorizontalScrollView
實作步驟
第一步:創建自定義類
package com.wust.myhorizontalscrollview;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.HorizontalScrollView;
public class mySlidingMenu extends HorizontalScrollView {
public mySlidingMenu(Context context) {
this(context,null);
}
public mySlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}

第二步:參考布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="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:orientation="horizontal"
tools:context=".MainActivity">
<com.wust.myhorizontalscrollview.mySlidingMenu
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是選單"
android:background="#f00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是內容"
android:background="#0f0"/>
</com.wust.myhorizontalscrollview.mySlidingMenu>
</LinearLayout>
如果大家在這一步試圖去運行,就會發現報錯,如下:
大概意思就是說 HorizontalScrollView 里面只能包一個布局,所以,我們得把上面的布局檔案修改一下,用一個線性布局把他們包起來
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="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:orientation="horizontal"
tools:context=".MainActivity">
<com.wust.myhorizontalscrollview.mySlidingMenu
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是選單"
android:background="#f00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是內容"
android:background="#0f0"/>
</LinearLayout>
</com.wust.myhorizontalscrollview.mySlidingMenu>
</LinearLayout>
第三步:撰寫邏輯
這個時候當我們運行程式的時候就會發現,我們明明寫的 match_parent 可是最后的結果不是鋪滿的,所以,我們得給我們的自定義組件賦值寬高,在 onMeasure() 方法里??我們不妨來試試

package com.wust.myhorizontalscrollview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
public class mySlidingMenu extends HorizontalScrollView {
private int mScreenWidth;
private View mMenu;
private View mContent;
private int mMenuWidth;
private int mContentWidth;
private int mScrennHeight;
public mySlidingMenu(Context context) {
this(context,null);
}
public mySlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取螢屏寬度
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScrennHeight = metrics.heightPixels;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//初始化寬度、高度
int width = 0;
int height = 0;
//獲取子代
ViewGroup ll_child = (ViewGroup) getChildAt(0);
mMenu = ll_child.getChildAt(0);
mContent = ll_child.getChildAt(1);
//設定選單 內容的寬度
mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - 400;
mContentWidth = mContent.getLayoutParams().width = mScreenWidth;
//設定自定義組件的寬高
width = mMenuWidth + mContentWidth;
height = mScrennHeight;
setMeasuredDimension(width,height);
}
}
我們發現布局結構的確出來了,但是劃不動, 最大的可能就是我們設定寬高的時機不對,通過翻閱資料,我發現我們應該寫在布局決議完成這個時間節點

package com.wust.myhorizontalscrollview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
public class mySlidingMenu extends HorizontalScrollView {
private int mScreenWidth;
private View mMenu;
private View mContent;
private int mMenuWidth;
private int mContentWidth;
private int mScrennHeight;
public mySlidingMenu(Context context) {
this(context,null);
}
public mySlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取螢屏寬度
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScrennHeight = metrics.heightPixels;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//獲取子代
ViewGroup ll_child = (ViewGroup) getChildAt(0);
mMenu = ll_child.getChildAt(0);
mContent = ll_child.getChildAt(1);
//設定選單 內容的寬度
mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - 400;
//設定布局寬度的第二種方法
ViewGroup.LayoutParams contentLayoutParams = mContent.getLayoutParams();
mContentWidth = contentLayoutParams.width = mScreenWidth;
mContent.setLayoutParams(contentLayoutParams);
}
}
這個時候,你就會發現螢屏可以滑動了

第四步:優化
經過上面三步,側滑選單我們就做完了,下面所講是為了優化用戶體驗
- 優化一:最初渲染頁面的時候,選單欄是關閉的
在這一步中要注意 關閉選單的時機,即到底寫在那個函式里
package com.wust.myhorizontalscrollview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
public class mySlidingMenu extends HorizontalScrollView {
private int mScreenWidth;
private View mMenu;
private View mContent;
private int mMenuWidth;
private int mContentWidth;
private int mScrennHeight;
public mySlidingMenu(Context context) {
this(context,null);
}
public mySlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取螢屏寬度
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScrennHeight = metrics.heightPixels;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//獲取子代
ViewGroup ll_child = (ViewGroup) getChildAt(0);
mMenu = ll_child.getChildAt(0);
mContent = ll_child.getChildAt(1);
//設定選單 內容的寬度
mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - 400;
//設定布局寬度的第二種方法
ViewGroup.LayoutParams contentLayoutParams = mContent.getLayoutParams();
mContentWidth = contentLayoutParams.width = mScreenWidth;
mContent.setLayoutParams(contentLayoutParams);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
closeMenu();
}
private void closeMenu(){
smoothScrollTo(mMenuWidth,0);
}
private void openMenu(){
smoothScrollTo(0,0);
}
}
- 優化二:判斷手指抬起時是否超過選單寬度一半,超過即打開選單,沒有超過即關閉選單
在這一步中要區別于我們上講 仿QQ6.0主頁面側滑效果 中的滑動正負觀,在這我就不過多贅述,大家自己動手實踐就知道了
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch(ev.getAction()){
//自己處理 手指抬起事件 所以 return true
case MotionEvent.ACTION_UP:
{
//就是這里要注意,因為最開始 getScrollX() 的值是 mMenuWidth
if (getScrollX() > mMenuWidth/2){
closeMenu();
}else {
openMenu();
}
return true;
}
}
return super.onTouchEvent(ev);
}
- 優化三:處理快速滑動手勢
在這個里面主要用到了一個類 GestureDetector
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取螢屏寬度
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScrennHeight = metrics.heightPixels;
//第一步:在構造方法里創建 GestureDetector 類
mGestureDetector = new GestureDetector(context, new myGestureListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//第二步:在 onTouchEvent 中攔截快速滑動手勢
if (mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch(ev.getAction()){
//自己處理 手指抬起事件 所以 return true
case MotionEvent.ACTION_UP:
{
//就是這里要注意,因為最開始 getScrollX() 的值是 mMenuWidth
if (getScrollX() > mMenuWidth/2){
closeMenu();
}else {
openMenu();
}
return true;
}
}
return super.onTouchEvent(ev);
}
//第三步:撰寫 myGestureListener
private class myGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
System.out.println("velocityX ->" + velocityX);
if (menuIsOpen){
//選單打開狀態
if (velocityX < -500){
closeMenu();
return true;
}
}else {
//選單關閉狀態
if (velocityX > 500){
openMenu();
return true;
}
}
return false;
}
}
//在下面兩個方法中添加了選單是否打開標志
private void closeMenu(){
smoothScrollTo(mMenuWidth,0);
menuIsOpen = false;
}
private void openMenu(){
smoothScrollTo(0,0);
menuIsOpen = true;
}
- 優化四:根據滑動給內容頁添加陰影
其實要實作這個有以下兩種方法:
-
在 xml 中靜態添加 View 覆寫層
-
在 java 代碼中動態添加 View 覆寫層(復用性好,我們選擇這一種)
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//獲取子代
ViewGroup ll_child = (ViewGroup) getChildAt(0);
mMenu = ll_child.getChildAt(0);
mContent = ll_child.getChildAt(1);
//設定選單 內容的寬度
mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - 400;
//第一步:先把以前的內容 從線性布局中移除
ll_child.removeView(mContent);
//第二步:創建一個幀布局
FrameLayout contentFrameLayout = new FrameLayout(getContext());
//第三步:把以前的內容 加入到這個幀布局中
contentFrameLayout.addView(mContent);
//第四步:創建一個陰影 層
mShadeView = new View(getContext());
//給陰影層設定顏色
mShadeView.setBackgroundColor(Color.parseColor("#550000"));
//第五步:將這個陰影層加入幀布局中,覆寫在內容之上
contentFrameLayout.addView(mShadeView);
//第六步:給幀布局設定寬度
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.width = mScreenWidth;
contentFrameLayout.setLayoutParams(params);
//第七步:將這個幀布局添加進來
ll_child.addView(contentFrameLayout);
//第八步:將陰影層設為透明
mShadeView.setAlpha(0.0f);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//計算比例因子 1 -> 0
float rate = l/(float)mMenuWidth;
//根據比例因子設定透明度
mShadeView.setAlpha(1-rate);
}
最終效果

有償提問
如果大家覺得這篇文章幫助你了,可以支持一下,
有償提問



轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/282333.html
標籤:其他
