自定义 view 练习(1):用 HorizontalScrollView 实现自定义侧滑菜单
2016-11-23 10:30
435 查看
尝试着仿造 qq 的侧滑菜单,来练习对 HorizontalScrollView 和自定义 View 的使用。
如上图,我们要实现的布局包括左侧的 Menu 与右部的 Content。默认情况下,Menu 区域是隐藏的,只显示 Content。当滑动 Content 时,再显示出 Menu。
先实现 Menu 的布局:
相对应的,要创建一个继承自 HorizontalScrollView 的 Class 来自定义 View ,也就是这里的 SlidingMenu 。
那么 activity_main 代码如下:
这样就基本实现了我们开头的预览效果。
当然为了美观,可以设置 Title 隐藏。在 MainActivity 里设置:
即可。
(这里是继承自 AppCompatActivity 的写法,如果是继承自 Activity, 直接用
也是同样的效果。)
首先在 values 目录下新建 attr.xml:
这里的 rightPadding 就是自定义的属性, format 表示属性类型 。
接着回到 SlidingMenu,重新书写构造方法:
利用 TypedArray 来获取自定义的属性值,再对获取到的属性值做处理。
究竟有没有效果呢?现在可以去 activity_main 里设置一下属性啦:
先在跟布局引用:
mymenu是自定义的名称,可以随意命名。这句表示引入自定义属性文件。
接着设置 SlidingMenu 的属性:
再次运行,可以看到 Menu 离屏幕右侧边距确实改变了。
实现基本布局
因为是对侧滑菜单的练习,QQ 那些复杂的布局就不一一实现了,这里直接用背景图来代替。如上图,我们要实现的布局包括左侧的 Menu 与右部的 Content。默认情况下,Menu 区域是隐藏的,只显示 Content。当滑动 Content 时,再显示出 Menu。
先实现 Menu 的布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/img_frame_background"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/left_menu_img1" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_1" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/left_menu_img1" android:layout_toRightOf="@id/left_menu_img1" android:text="@string/first_item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/left_menu_img2" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_2" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/left_menu_img2" android:layout_toRightOf="@id/left_menu_img2" android:text="@string/second_item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/left_menu_img3" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_3" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/left_menu_img3" android:layout_toRightOf="@id/left_menu_img3" android:text="@string/third_item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/left_menu_img4" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_4" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/left_menu_img4" android:layout_toRightOf="@id/left_menu_img4" android:text="@string/forth_item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView 4000 android:id="@+id/left_menu_img5" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_5" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/left_menu_img5" android:layout_toRightOf="@id/left_menu_img5" android:text="@string/fifth_item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> </LinearLayout> </RelativeLayout>
相对应的,要创建一个继承自 HorizontalScrollView 的 Class 来自定义 View ,也就是这里的 SlidingMenu 。
那么 activity_main 代码如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.shmj.mouzhai.scrollerviewmenu.MainActivity"> <com.shmj.mouzhai.scrollerviewmenu.view.SlidingMenu android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal"> <include layout="@layout/left_menu"/> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/qq"/> </LinearLayout> </com.shmj.mouzhai.scrollerviewmenu.view.SlidingMenu> </RelativeLayout>
自定义 view
我们自定义的 SlidingMenu 应当重写 onMeasure() 、onLayout()、onTouchEvent() 方法,来让 Activity 启动时的 Menu 隐藏在屏幕之外,随着滑动逐渐显示。package com.shmj.mouzhai.scrollerviewmenu.view; import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.MotionEvent; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; /** * 自定义侧滑菜单 * <p> * Created by Mouzhai on 2016/11/22. */ public class SlidingMenu extends HorizontalScrollView { private LinearLayout mWrapper; private ViewGroup mMenu; private ViewGroup mContent; private int mScreenWidth; private int mMenuWidth; private int mMenuRightPadding = 50;//菜单距离右侧边距,单位 dp private boolean once = false; /** * 未设置自定义属性时,默认调用 */ public SlidingMenu(Context context, AttributeSet attrs) { super(context, attrs); WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); windowManager.getDefaultDisplay().getMetrics(displayMetrics); mScreenWidth = displayMetrics.widthPixels; //将 dp 转化为像素值 mMenuRightPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()); } /** * 设置自身及子 View 的宽和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (!once) { mWrapper = (LinearLayout) getChildAt(0); mMenu = (ViewGroup) mWrapper.getChildAt(0); mContent = (ViewGroup) mWrapper.getChildAt(1); mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding; mContent.getLayoutParams().width = mScreenWidth; once = true; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 决定子 view 的位置 * 设置偏移量来隐藏 menu */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { this.scrollTo(mMenuWidth, 0); } } @Override public boolean onTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_UP: int scrollX = getScrollX();//隐藏在屏幕左侧之外的宽度 //如果隐藏宽度大于一半则隐藏 menu,否则显示 if (scrollX >= mMenuWidth / 2) { this.smoothScrollTo(mMenuWidth, 0); } else { this.smoothScrollTo(0, 0); } return true; } return super.onTouchEvent(ev); } }
这样就基本实现了我们开头的预览效果。
当然为了美观,可以设置 Title 隐藏。在 MainActivity 里设置:
if(getSupportActionBar() != null){ getSupportActionBar().hide(); }
即可。
(这里是继承自 AppCompatActivity 的写法,如果是继承自 Activity, 直接用
requestWindowFeature(Window.FEATURE_NO_TITLE);
也是同样的效果。)
自定义属性
但是这就完了吗?不,通常来说,自定义 view 也应当自定义一些属性,来方便使用。这里自定义 Menu 距离屏幕右侧的距离来作为练习。首先在 values 目录下新建 attr.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="SlidingMenu"> <attr name="rightPadding" format="dimension"/> </declare-styleable> </resources>
这里的 rightPadding 就是自定义的属性, format 表示属性类型 。
接着回到 SlidingMenu,重新书写构造方法:
public SlidingMenu(Context context) { this(context, null); } /** * 未设置自定义属性时,默认调用 */ public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * 使用了自定义属性时,调用此构造方法 */ public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取自定义属性 TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0); int n = typedArray.getIndexCount(); for(int i = 0; i<n; i++){ int attr = typedArray.getIndex(i); switch (attr){ case R.styleable.SlidingMenu_rightPadding: //默认的距离 //将 dp 转化为像素值 int defaultPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()); mMenuRightPadding = typedArray.getDimensionPixelSize(attr, defaultPadding); break; } } typedArray.recycle(); WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); windowManager.getDefaultDisplay().getMetrics(displayMetrics); mScreenWidth = displayMetrics.widthPixels; }
利用 TypedArray 来获取自定义的属性值,再对获取到的属性值做处理。
究竟有没有效果呢?现在可以去 activity_main 里设置一下属性啦:
先在跟布局引用:
xmlns:mymenu="http://schemas.android.com/apk/res-auto"
mymenu是自定义的名称,可以随意命名。这句表示引入自定义属性文件。
接着设置 SlidingMenu 的属性:
android:layout_width="match_parent" android:layout_height="match_parent" mymenu:rightPadding="200dp"
再次运行,可以看到 Menu 离屏幕右侧边距确实改变了。
相关文章推荐
- android:自定义HorizontalScrollView实现qq侧滑菜单
- Android学习笔记 :自定义HorizontalScrollView实现侧滑菜单
- android:自定义HorizontalScrollView实现qq侧滑菜单 标签: HorizontalScrollView自定义viewqq侧滑菜单 2016
- 使用HorizontalScrollView实现侧滑菜单
- 使用HorizontalScrollView实现最简单的侧滑菜单
- 仿QQ侧滑菜单,HorizontalScrollView和DrawerLayout实现对比
- Android 自定义HorizontalScrollView实现二级菜单
- 自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)
- SlidingMenu重写HorizontalScrollView实现菜单侧滑的效果
- 利用HorizontalScrollView实现仿QQ的侧滑菜单
- 利用HorizontalScrollView实现侧滑菜单
- 安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)
- Android 高仿 QQ5.0 侧滑菜单效果 HorizontalScrollView
- Android自定义LinearLayout实现左右侧滑菜单,完美兼容ListView、ScrollView、ViewPager等滑动控件
- 使用HorizontalScrollView实现侧滑效果(2)
- 自定义ViewPager、HorizontalScrollView、Indicator实现可滑动的头部
- Android 实现横向标题栏滚动效果(HorizontalScrollView + GridView + Viewpager + 自定义适配器)
- 安卓笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑结果)
- 使用HorizontalScrollView实现侧滑效果(3)
- Android 自定义View修炼-仿QQ5.0 的侧滑菜单效果的实现