Android自定義View 仿QQ側(cè)滑菜單的實(shí)現(xiàn)代碼
先看看QQ的側(cè)滑效果

分析一下
先上原理圖(不知道能否表達(dá)的清楚 ==)
-首先這里使用了 Android 的HorizontalScrollView 水平滑動(dòng)布局作為容器,當(dāng)然我們需要繼承它自定義一個(gè)側(cè)滑視圖
- 這個(gè)容器里面有一個(gè)父布局(一般用LinerLayout,本demo用的是),這個(gè)父布局里面有且只有兩個(gè)子控件(布局),初始狀態(tài)菜單頁(yè)的位置在Y軸上存在偏移這樣可以就可以形成主頁(yè)疊在菜單頁(yè)的上方的視覺(jué)效果;然后在滑動(dòng)的過(guò)程程中 逐漸修正偏移,最后菜單頁(yè)和主頁(yè)并排排列。原理搞清了實(shí)現(xiàn)起來(lái)就不是事兒了……
具體實(shí)現(xiàn)
布局代碼
<fierce_luk.com.sideslipviewdemo2.SideslipView
android:id="@+id/my_veiw"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
luk:leftPanding="200dp">
<!--如果菜單在左邊直接用 LinearLayout-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/image2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/homepage"
android:gravity="center"
android:tag="0"
android:text="菜單"
android:textColor="@color/colorAccent"
android:textSize="60sp" />
<TextView
android:id="@+id/image1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color1"
android:gravity="center"
android:tag="1"
android:text="主頁(yè)面"
android:textColor="@color/colorAccent"
android:textSize="60sp" />
</FrameLayout>
<!--<fragment-->
<!--android:name="com.luk.bluetoothapp.fragment.MyBluetoothDevice"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:tag="1" />-->
<!--<fragment-->
<!--android:name="com.luk.bluetoothapp.fragment.HomeFragment"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:tag="0" />-->
</fierce_luk.com.sideslipviewdemo2.SideslipView>
自定義的側(cè)滑視圖
最核心的部分
public class SideslipView extends HorizontalScrollView {
private int mScreenWidth;//屏幕寬度
private int mMenuLeftPadding;
private int mBluetoothWidth;//菜單的寬度
private int mHalfMenuWidth;
View home;
View bluetooth;
protected boolean isOpen;
protected boolean isFirst = true;
public SideslipView(Context context) {
// super 改 this
this(context, null);
}
public SideslipView(Context context, AttributeSet attrs) {
// super 改 this
this(context, attrs, 0);
}
public SideslipView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//測(cè)量屏幕寬度
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
// mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
Log.e("TAG", "MyScrollView: mScreenWidth" + mScreenWidth);
//獲取 自定義的屬性值
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyScrollView);
int n = a.length();
for (int i = 0; i < n; i++) {
int arrt = a.getIndex(i);
switch (arrt) {
case R.styleable.MyScrollView_leftPanding:
mMenuLeftPadding = (int) a.getDimension(R.styleable.MyScrollView_leftPanding, 0);
break;
default:
break;
}
}
Log.e("TAG", "MyScrollView: mMenuLeftPadding" + mMenuLeftPadding);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (isFirst) {
//獲取子布局 并設(shè)置寬度
//如果菜單在左邊 用LinearLayout就行
// LinearLayout layout = (LinearLayout) this.getChildAt(0);
/**此處因?yàn)?把側(cè)邊拉出頁(yè)面設(shè)置在了右邊 所有用 FrameLayout
* 不然在設(shè)置偏移量時(shí) 隱藏的側(cè)邊菜單會(huì)跑到主頁(yè)面的上面*/
FrameLayout layout = (FrameLayout) this.getChildAt(0);
home = layout.getChildAt(1);
bluetooth = layout.getChildAt(0);
LayoutParams params = new LayoutParams(mBluetoothWidth,
getResources().getDisplayMetrics().heightPixels);
params.setMargins(mScreenWidth, 0, 0, 0);
bluetooth.setLayoutParams(params);
mBluetoothWidth = mScreenWidth - mMenuLeftPadding;
home.getLayoutParams().width = mScreenWidth;
bluetooth.getLayoutParams().width = mBluetoothWidth;
mHalfMenuWidth = mBluetoothWidth / 2;
isFirst = false;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//首先隱藏 Bluetooth
if (changed)
this.scrollTo(0, mBluetoothWidth);
}
Animation anim;
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
if (scrollX >= mHalfMenuWidth) {
Log.e("TAG", "====");
this.smoothScrollTo(mBluetoothWidth, 0);
isOpen = true;
} else {
this.smoothScrollTo(0, 0);
isOpen = false;
}
//必須消耗事件
return true;
}
return super.onTouchEvent(ev);
//return true;
}
/**
* 打開(kāi)菜單欄
*/
protected void openMenu() {
if (isOpen)
return;
this.smoothScrollTo(mBluetoothWidth, 0);
isOpen = true;
}
/**
* 關(guān)閉菜單欄
*/
protected void closeMenu() {
if (!isOpen)
return;
this.smoothScrollTo(0, 0);
isOpen = false;
}
/**
* 按鈕切換菜單
*/
public void toggleMenu() {
if (isOpen)
closeMenu();
else
openMenu();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//此處 l 起始值為零(沒(méi)有偏移)
Log.e("TAG", "l=" + l + " t=" + t);
Log.e("TAG", "oldl=" + oldl + " oldt=" + oldt);
// scale 在 1 到 0 之間
float scale = l * 1.0f / mBluetoothWidth;
/**
* 抽屜式側(cè)滑
* scaleLeft 從默認(rèn)偏移量到偏移量 為零
*實(shí)現(xiàn)
* */
float scaleLeft = 0.4f - 0.4f * scale;
/**設(shè)置 X 軸方向的偏移量**/
ViewHelper.setTranslationX(bluetooth, -(mBluetoothWidth * scaleLeft));
Log.e("TAG", "mBluetoothWidth+" + mBluetoothWidth);
Log.e("TAG", "=============" + mBluetoothWidth * scale + "scale" + scale);
/*
*//**設(shè)置縮放時(shí)的 軸心點(diǎn)**//*
//此處軸心為右邊界的中點(diǎn)
ViewHelper.setPivotY(home, mScreenWidth);
ViewHelper.setPivotX(home, home.getHeight() / 2);
*//**設(shè)置 XY軸方向的 縮放動(dòng)畫(從 1 到 0.9)**//*
float pivoXY = 1 - 0.4f * scale;
ViewHelper.setScaleX(home, pivoXY);
ViewHelper.setScaleY(home, pivoXY);*/
/*
*//**設(shè)置透明度**//*
//從 1 到 0.6;
float alpha = 1 - 0.4f * scale;
ViewHelper.setAlpha(home, alpha);*/
}
}
擴(kuò)展
添加之定義屬性 讓用戶配置菜單距離右邊的邊距的值;
首先在values文件夾下新建一個(gè)attr.xml,寫入以下內(nèi)容:
<resources>
<declare-styleable name="MyScrollView">
<attr name="rightPanding" format="dimension" />
<attr name="leftPanding" format="dimension" />
</declare-styleable>
</resources>
在布局里設(shè)置邊距
<fierce_luk.com.sideslipviewdemo2.SideslipView
android:id="@+id/my_veiw"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
luk:leftPanding="200dp">
其他的就不贅述了,看看效果
- 源碼下載Demo源碼點(diǎn)擊下載

總結(jié)
以上所述是小編給大家介紹的Android自定義View 仿QQ側(cè)滑菜單的實(shí)現(xiàn)代碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android 使用Glide加載網(wǎng)絡(luò)圖片等比例縮放的實(shí)現(xiàn)方法
這篇文章主要介紹了Android 使用Glide加載網(wǎng)絡(luò)圖片等比例縮放的實(shí)現(xiàn)方法,需要的朋友可以參考下2018-08-08
Android使用BottomTabBar實(shí)現(xiàn)底部導(dǎo)航頁(yè)效果
這篇文章主要介紹了Android使用BottomTabBar實(shí)現(xiàn)底部導(dǎo)航頁(yè)效果,本文通過(guò)實(shí)例代碼結(jié)合文字說(shuō)明的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧2018-03-03
adnroid已安裝應(yīng)用中檢測(cè)某應(yīng)用是否安裝的代碼實(shí)例
這篇文章主要介紹了Android怎么檢測(cè)一個(gè)應(yīng)用是否安裝的方法,大家參考使用吧2013-11-11
kotlin實(shí)戰(zhàn)教程之lambda編程
這篇文章主要給大家介紹了關(guān)于kotlin實(shí)戰(zhàn)教程之lambda編程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用kotlin具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09
Android中自定義PopupWindow實(shí)現(xiàn)彈出框并帶有動(dòng)畫效果
這篇文章主要介紹了Android中自定義PopupWindow實(shí)現(xiàn)彈出框并帶有動(dòng)畫效果的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
Android底部菜單欄實(shí)現(xiàn)的實(shí)例代碼
這篇文章主要介紹了Android底部菜單欄實(shí)現(xiàn)的實(shí)例代碼,本文通過(guò)使用RadioGroup來(lái)實(shí)現(xiàn)底部導(dǎo)航菜單欄。現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Android 圖片存入系統(tǒng)相冊(cè)更新顯示實(shí)例詳解
這篇文章主要介紹了Android 圖片存入系統(tǒng)相冊(cè)更新顯示實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android RecycleView實(shí)現(xiàn)Item拖拽效果
RecyclerView是Android一個(gè)更強(qiáng)大的控件,其不僅可以實(shí)現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。本文將介紹通過(guò)RecyclerView實(shí)現(xiàn)Item拖拽效果以及拖拽位置保存,感興趣的可以參考一下2022-01-01
Android6.0編程實(shí)現(xiàn)雙向通話自動(dòng)錄音功能的方法詳解
這篇文章主要介紹了Android6.0編程實(shí)現(xiàn)雙向通話自動(dòng)錄音功能的方法,結(jié)合實(shí)例形式分析了Android錄音功能的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-07-07


