Android自己定义组件系列【3】——自己定义ViewGroup实现側滑
有关自己定义ViewGroup的文章已经非常多了,我为什么写这篇文章,对于刚開始学习的人或者对自己定义组件比較生疏的朋友尽管能够拿来主义的用了,可是要一步一步的实现和了解当中的过程和原理才干真真脱离别人的代码,举一反三却不easy,非常多博主事实上不愿意一步一步的去写,这样非常耗时,可是假设能对读者有帮助,能从这篇文章中学会自己定义组件就达到我的目的了。
第一步:搭建框架来实现一个3/5和2/5分屏的界面,效果例如以下:
最外层是一个自己定义的ViewGroup布局文件例如以下:
- package com.example.testscrollto;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.View.MeasureSpec;
- public class MyScrollView extends ViewGroup{
- private int mWidth;
- private int mHeight;
- private float mMenuWeight = 3.0f / 5; //菜单界面比例
- private View mMenuView; //菜单界面
- private View mPriView; //内容界面
- public MyScrollView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- @Override
- protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
- System.out.println("运行了onLayout");
- mMenuView.layout(0, 0, (int)(mWidth * mMenuWeight), mHeight);
- mPriView.layout((int)(mWidth * mMenuWeight), 0, mWidth, mHeight);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- System.out.println("运行了onMeasure");
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- /*
- * onMeasure传入的两个參数是由上一层控件传入的大小,有多种情况,重写该方法时须要对计算控件的实际大小,
- * 然后调用setMeasuredDimension(int, int)设置实际大小。
- * onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。
- * 我们须要通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,
- * 用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
- * mode共同拥有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
- * MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为详细数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
- * MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸仅仅要不超过父控件同意的最大尺寸就可以。因此,此时的mode是AT_MOST,size给出了父控件同意的最大尺寸。
- * MeasureSpec.UNSPECIFIED是未指定尺寸,这样的情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
- */
- mWidth = MeasureSpec.getSize(widthMeasureSpec); //获取MyScrollView的宽度
- mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取MyScrollView的高度
- }
- /**设置右滑的菜单View*/
- public void setMenu(View menu){
- mMenuView = menu;
- addView(mMenuView);
- }
- /**
- * 设置主界面View
- */
- public void setPrimary(View primary){
- mPriView = primary;
- addView(mPriView);
- }
- }
第二步:按须要设置界面位置
- @Override
- protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
- mMenuView.layout(-(int)(mWidth * mMenuWeight), 0, 0, mHeight);
- mPriView.layout(0, 0, mWidth, mHeight);
- }
第三步:实现左右滑动
- package com.example.testscrollto;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.Scroller;
- public class MyScrollView extends ViewGroup{
- private Context mContext;
- private int mWidth;
- private int mHeight;
- private float mMenuWeight = 3.0f / 5; //菜单界面比例
- private View mMenuView; //菜单界面
- private View mPriView; //内容界面
- private boolean mIsShowMenu;
- private Scroller mScroller;
- public MyScrollView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- mScroller = new Scroller(mContext);
- }
- @Override
- protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
- mMenuView.layout(-(int)(mWidth * mMenuWeight), 0, 0, mHeight);
- mPriView.layout(0, 0, mWidth, mHeight);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mWidth = MeasureSpec.getSize(widthMeasureSpec); //获取MyScrollView的宽度
- mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取MyScrollView的高度
- }
- /**设置右滑的菜单View*/
- public void setMenu(View menu){
- mMenuView = menu;
- addView(mMenuView);
- }
- /**
- * 设置主界面View
- */
- public void setPrimary(View primary){
- mPriView = primary;
- addView(mPriView);
- }
- private float mDownX;
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float x = event.getX();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- System.out.println("ACTION_DOWN");
- mDownX = x; //记录按下时的x坐标
- break;
- case MotionEvent.ACTION_UP:
- System.out.println("ACTION_UP");
- int dis = (int) (x - mDownX); //滑动的距离
- if(Math.abs(dis) > (mWidth * mMenuWeight / 2)){
- if(dis > 0){ //假设>0则是向右滑动
- showMenu();
- }else{ //假设<0则是向左滑动
- hideMenu();
- }
- }
- break;
- default:
- break;
- }
- return true;
- }
- @Override
- public void computeScroll() {
- super.computeScroll();
- if(mScroller.computeScrollOffset()){
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
- postInvalidate();
- }
- }
- public boolean isShowMenu(){
- return mIsShowMenu;
- }
- public void showMenu(){
- if(mIsShowMenu){
- return;
- }
- mIsShowMenu = true; //标记菜单已经显示
- int dx = (int)(mWidth * mMenuWeight); //滑动到目标位置的距离
- mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
- invalidate();
- }
- public void hideMenu(){
- if(!mIsShowMenu){
- return;
- }
- mIsShowMenu = false;
- int dx = (int)(mWidth * mMenuWeight);
- mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
- invalidate();
- }
- }
从上面代码中能够看到以下两句代码触发computeScroll()方法
- mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
- invalidate();
第四步:加入窗体状态切换监听接口
- public interface OnMenuChangedListener{
- public void onChanged(boolean isShow);
- }
- public void setOnMenuChangedListener(OnMenuChangedListener listener){
- mListener = listener;
- }
将showMenu()方法和hideMenu()方法改动例如以下:
- public void showMenu(){
- if(mIsShowMenu){
- return;
- }
- mIsShowMenu = true;
- int dx = (int)(mWidth * mMenuWeight);
- mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
- if(mListener != null){
- mListener.onChanged(mIsShowMenu);
- }
- invalidate();
- }
- public void hideMenu(){
- if(!mIsShowMenu){
- return;
- }
- mIsShowMenu = false;
- int dx = (int)(mWidth * mMenuWeight);
- mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
- if(mListener != null){
- mListener.onChanged(mIsShowMenu);
- }
- invalidate();
- }
在MainActivity中加入监听
- mScrollView.setOnMenuChangedListener(new OnMenuChangedListener() {
- @Override
- public void onChanged(boolean isShow) {
- System.out.println("窗体切换了一次");
- }
- });
第五步:依据详细业务及需求实现菜单列表及界面(直接贴出代码)
MainActivity.java
- package com.example.testrefreshview;
- import android.app.Activity;
- import android.graphics.Color;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.AdapterView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.ListView;
- import com.example.testrefreshview.RightScrollView.OnMenuChangedListener;
- /**
- * 測试具有右滑菜单功能的ViewGroup,RigthScrollView
- *@author Lqh
- */
- public class MainActivity extends Activity {
- private RightScrollView mRightScrollView;
- private Button mShowMenuBtn;
- private ListView mMenuList;
- private ArrayAdapter<String> mAdapter;
- private String[] menus = {"附近的人", "我的资料", "设置", "游戏", "即时聊天"};
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.rightscrollview_test);
- mRightScrollView = (RightScrollView)findViewById(R.id.rightscrollview);
- final View menu = getLayoutInflater().inflate(R.layout.rightscrollview_menu, null);
- final View primary = getLayoutInflater().inflate(R.layout.rightscrollview_primary, null);
- mMenuList = (ListView) menu.findViewById(R.id.list_right_menu);
- mShowMenuBtn = (Button) primary.findViewById(R.id.btn_showmenu);
- mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, menus);
- mMenuList.setAdapter(mAdapter);
- mShowMenuBtn.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- if(mRightScrollView.isShowMenu()){
- mRightScrollView.hideMenu();
- }else{
- mRightScrollView.showMenu();
- }
- }
- });
- mRightScrollView.setOnMenuChangedListener(new OnMenuChangedListener() {
- public void onChanged(boolean isShow) {
- if(isShow){
- mShowMenuBtn.setText("隐藏菜单");
- }else{
- mShowMenuBtn.setText("显示菜单");
- }
- }
- });
- mRightScrollView.setMenu(menu);
- mRightScrollView.setPrimary(primary);
- mMenuList.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- switch(position){
- case 0:
- primary.setBackgroundColor(Color.CYAN);
- break;
- case 1:
- primary.setBackgroundColor(Color.BLUE);
- break;
- case 2:
- primary.setBackgroundColor(Color.GRAY);
- break;
- case 3:
- primary.setBackgroundColor(Color.MAGENTA);
- break;
- case 4:
- primary.setBackgroundColor(Color.YELLOW);
- break;
- }
- mRightScrollView.hideMenu();
- }
- });
- }
- }
RightScrollView.java
- package com.example.testrefreshview;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.Scroller;
- /**
- * 具有右滑菜单的ViewGroup,相似于Facebook的主界面
- * @author Lqh
- */
- public class RightScrollView extends ViewGroup {
- private Context mContext;
- private Scroller mScroller;
- private View mMenuView;
- private View mPriView;
- private int mWidth;
- private int mHeight;
- private boolean mIsShowMenu;
- private float mMenuWeight = 3.0f / 5;
- private OnMenuChangedListener mListener;
- public RightScrollView(Context context) {
- this(context, null);
- }
- public RightScrollView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- mScroller = new Scroller(mContext);
- }
- /**设置右滑的菜单View*/
- public void setMenu(View menu){
- mMenuView = menu;
- addView(mMenuView);
- }
- /**
- * 设置主界面View
- */
- public void setPrimary(View primary){
- mPriView = primary;
- addView(mPriView);
- }
- public boolean isShowMenu(){
- return mIsShowMenu;
- }
- public void setOnMenuChangedListener(OnMenuChangedListener listener){
- mListener = listener;
- }
- public void showMenu(){
- if(mIsShowMenu){
- return;
- }
- mIsShowMenu = true;
- int dx = (int)(mWidth * mMenuWeight);
- mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
- if(mListener != null){
- mListener.onChanged(mIsShowMenu);
- }
- invalidate();
- }
- public void hideMenu(){
- if(!mIsShowMenu){
- return;
- }
- mIsShowMenu = false;
- int dx = (int)(mWidth * mMenuWeight);
- mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
- if(mListener != null){
- mListener.onChanged(mIsShowMenu);
- }
- invalidate();
- }
- private float mDownX;
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float x = event.getX();
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
- mDownX = x;
- break;
- case MotionEvent.ACTION_UP:
- int dis = (int) (x - mDownX);
- if(Math.abs(dis) > (mWidth * mMenuWeight / 2)){
- if(dis > 0){
- showMenu();
- }else{
- hideMenu();
- }
- }
- break;
- }
- return true;
- }
- @Override
- public void computeScroll() {
- super.computeScroll();
- if(mScroller.computeScrollOffset()){
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
- postInvalidate();
- }
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mWidth = MeasureSpec.getSize(widthMeasureSpec);
- mHeight = MeasureSpec.getSize(heightMeasureSpec);
- setMeasuredDimension(mWidth, mHeight);
- int widthSpec = MeasureSpec.makeMeasureSpec((int)(mWidth * mMenuWeight), MeasureSpec.EXACTLY);
- int heightSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
- mMenuView.measure(widthSpec, heightSpec);
- widthSpec = MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY);
- mPriView.measure(widthSpec, heightSpec);
- }
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- mMenuView.layout(-(int)(mWidth * mMenuWeight), 0, 0, mHeight);
- mPriView.layout(0, 0, mWidth, mHeight);
- }
- public interface OnMenuChangedListener{
- public void onChanged(boolean isShow);
- }
- }
运行效果:
Android自己定义组件系列【3】——自己定义ViewGroup实现側滑的更多相关文章
- Android自己定义组件系列【7】——进阶实践(4)
上一篇<Android自己定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识.这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpa ...
- Android自己定义组件系列【6】——进阶实践(3)
上一篇<Android自己定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计 ...
- Android自己定义组件系列【5】——进阶实践(2)
上一篇<Android自己定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这 ...
- Android自己定义组件系列【4】——自己定义ViewGroup实现双側滑动
在上一篇文章<Android自己定义组件系列[3]--自己定义ViewGroup实现側滑>中实现了仿Facebook和人人网的側滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布 ...
- Android自己定义组件系列【2】——Scroller类
在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友能够先看<自己定义View及ViewGroup> scrollTo和scrollBy尽管实 ...
- Android自己定义组件系列【1】——自己定义View及ViewGroup
View类是ViewGroup的父类,ViewGroup具有View的全部特性.ViewGroup主要用来充当View的容器.将当中的View作为自己孩子,并对其进行管理.当然孩子也能够是ViewGr ...
- Android自己定义组件系列【9】——Canvas绘制折线图
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了非常多插件,可是非常多时候我们须要依据详细项目自己定义这些图表,这一篇文章我们一起来看看怎样在Android中使用Can ...
- Android自己定义组件系列【5】——高级实践(1)
在接下来的几篇文章将任老师的博文<您可以下拉PinnedHeaderExpandableListView实现>骤来具体实现.来学习一下大神的代码并记录一下. 原文出处:http://blo ...
- Android自己定义组件系列【8】——面膜文字动画
我们掩盖文字动画Flash中非经货共同体共同,由于Android应用程序开发人员做你想要做这个动画在应用程序中去?本文中,我们看的是如何自己的定义ImageView来实现让一张文字图片实现文字的遮罩闪 ...
随机推荐
- git —— bug分支
储藏工作现场 $ git stash 切换到需要修改bug的分支,创建临时分支 修复bug,修复完提交 修复完之后,切换到需要修改的分支.完成合并 合并后删除临时分支 完成后,可以重新回到没有修改完的 ...
- python基础学习之路No.4 数据转换以及操作
练习python的时候经常会用到一些不同数据类型之间的转换操作 搜集了一些资料,整理如下 函数 描述 int(x [,base]) 将x转换为一个整数 long(x [,base] ) 将x转换为一个 ...
- JavaScript之MV*模式
前言 在前端技术的快速发展过程中,MVC(模型-视图-控制器).MVP(模型-视图-表示器)和MVVM(模型-视图-视图模型)也得到了很多使用.然而,这三种框架模式非常相似,不易分清,易于混淆.本文重 ...
- sql server 约束 查找
--1.主键约束 SELECT tab.name AS [表名], idx.name AS [主键名称], col.name AS [主键列名] FROM sys.indexes idx JOIN s ...
- 在SQL中有时候我们需要查看现在正在SQL Server执行的命令
在SQL中有时候我们需要查看现在正在SQL Server执行的命令.在分析管理器或者Microsoft SQL Server Management Studio中,我们可以在"管理-SQL ...
- MySQL学习笔记:生成时间维度表
# ---- 对应时间戳怎么生成的? ---- /*TIME_CD TIME_CD1000000 000005000001 000005000002 000005000003 000005000004 ...
- CVE-2010-2553 Microsoft Windows Cinepak 编码解码器解压缩漏洞 分析
Microsoft Windows是微软发布的非常流行的操作系统. Microsoft Windows XP SP2和SP3,Windows Vista SP1和SP2,以及Win ...
- Pg168-1
1.Pg168-1 package org.hanqi.pn0120; public class Computer { private double neicundx=10; public doubl ...
- https页面打不开
https://blog.csdn.net/leedaning/article/details/71552625
- abp zero 4.3 发布
Demo URL: http://abpzerodemo.demo.aspnetzero.com Username: systemPassword: 123456 需要源码,请加QQ:3833-255 ...