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);
}

}

运行效果:



源码下载:http://download.csdn.net/detail/lxq_xsyu/7231677

时间: 2024-12-16 16:22:03

Android自己定义组件系列【3】——自己定义ViewGroup实现側滑的相关文章

Android自己定义组件系列【6】——进阶实践(3)

上一篇<Android自己定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计划中间插一段"知识点",对Android中的事件分发机制进行解析.细心的朋友可能会发现,打开大牛写的Android项目,里面非常多组件都是自己定义的(这就是为什么界面和体验这么吸引你的原因),可是要灵活的去自己定义组件就必须对手势(也就是各种监听)必须熟悉,能处理好事件之间的关系. 先看一

Android自己定义组件系列【4】——自己定义ViewGroup实现双側滑动

在上一篇文章<Android自己定义组件系列[3]--自己定义ViewGroup实现側滑>中实现了仿Facebook和人人网的側滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示意图: 2.核心代码 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth

Android自己定义组件系列【5】——进阶实践(2)

上一篇<Android自己定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这一篇我们来看看ExpandableListView的使用并实现剩下的部分. 原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871 一.ExpandableListView的使用方法 ExpandableListView是ListVi

Android自己定义组件系列【2】——Scroller类

在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友能够先看<自己定义View及ViewGroup> scrollTo和scrollBy尽管实现了视图的偏移,可是却没有更好的控制移动过程,移动是瞬间进行的.Scroller类就是为解决问题而设计的. 打开Scroller的源码,能够看到startScroll方法: /** * Start scrolling by providing a starting point and the distance

Android自己定义组件系列【1】——自己定义View及ViewGroup

View类是ViewGroup的父类,ViewGroup具有View的全部特性.ViewGroup主要用来充当View的容器.将当中的View作为自己孩子,并对其进行管理.当然孩子也能够是ViewGroup类型. View类一般用于画图操作,重写它的onDraw方法,但它不能够包括其它组件,没有addView(View view)方法. ViewGroup是一个组件容器,它能够包括不论什么组件,但必须重写onLayout(boolean changed,int l,int t,int r,int

Android自己定义组件系列【8】——面膜文字动画

我们掩盖文字动画Flash中非经货共同体共同,由于Android应用程序开发人员做你想要做这个动画在应用程序中去?本文中,我们看的是如何自己的定义ImageView来实现让一张文字图片实现文字的遮罩闪烁效果,以下先来看看效果吧. (录屏幕延时导致效果看起来不是非常好) 一.实现原理 实现原理是重写View的onCreate方法.获取图片资源后对每一个像素的透明度进行改动来实现,再启动一个线程来循环改变某个区域中的像素透明度. RGBA基础知识:(以下几段介绍文字引用自维基百科) RGBA是代表R

Android自己定义组件系列【5】——高级实践(1)

在接下来的几篇文章将任老师的博文<您可以下拉PinnedHeaderExpandableListView实现>骤来具体实现.来学习一下大神的代码并记录一下. 原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871 先看一下终于效果: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2luZ3doYXRpd2FubmE=/font/5a6L5L2T/fontsize/400/f

Android 使用DrawerLayout高速实现側滑菜单

一.概述 DrawerLayout是一个能够方便的实现Android側滑菜单的组件,我近期开发的项目中也有一个側滑菜单的功能.于是DrawerLayout就派上用场了.假设你从未使用过DrawerLayout.那么本篇博客将使用一个简单的案例带你迅速掌握DrawerLayout的使用方法. 二.效果图 三.代码实现 主布局activity_main.xml <? xml version="1.0" encoding="utf-8"? > <Line

Android画图系列(二)——自己定义View绘制基本图形

这个系列主要是介绍下Android自己定义View和Android画图机制.自己能力有限.假设在介绍过程中有什么错误.欢迎指正 前言 在上一篇Android画图系列(一)--自己定义View基础中我们了解自己定义View相关的基本知识.只是,这些东西依然还是理论,接下来我们就实际绘制一些东西 在本篇文章中,我们先了解下面Canvas,而且画一些主要的图形 Canvas简单介绍 Canvas我们能够称之为画布.能够在上面绘制各种东西.是安卓平台2D图形绘制的基础.非常强大. 一般来说,比較基础的东