在自定義控件——初識(shí)自定義控件里面飘哨,我們已經(jīng)對(duì)自定義控件進(jìn)行描述和分類统屈。其分類分別是
- 自制控件
- 組合控件
- 拓展控件
這篇博文里面孽拷,我們繼續(xù)進(jìn)行自制控件膜宋。
我們想要繼續(xù)的是一個(gè)簡(jiǎn)單的仿造qq側(cè)滑菜單秋茫。
先來(lái)看一下效果圖
在(初識(shí)自定義控件)中乃秀,我們知道了自定義控件分為三種
- 自制控件
- 組合控件
- 拓展控件
在(自制控件1)中,我們自制了一個(gè)開(kāi)關(guān)按鈕View跺讯,這次,我們來(lái)做自制的ViewGroup抬吟,一個(gè)簡(jiǎn)單的仿qq策劃菜單。
在(自制控件1)我們利用View.layout(l,t,r,b)這個(gè)api讓View動(dòng)起來(lái)火本。在本次的側(cè)滑菜單里面擎析,我們使用
ScrollTo和ScrollBy讓View動(dòng)起來(lái)
而且使用Scroller做彈性滑動(dòng)桨醋。
如果對(duì)自制繼承自ViewGroup的控件還沒(méi)有一個(gè)大概的概念现斋,可以通過(guò)(初識(shí)自定義控件)這篇博文里面的demo喜最,進(jìn)行一個(gè)大概的了解。
一庄蹋、造起來(lái)一個(gè)ViewGroup
新建一個(gè)類瞬内,比如叫做SlideMenu迷雪,繼承自ViewGroup
public class SlideMenu extends ViewGroup{
public SlideMenu(Context context) {
super(context);
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
.
.
然后我們?cè)?activity_main 利用控件的全路徑名引入這個(gè)控件
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.amqr.diyslidemenu.MainActivity">
<com.amqr.diyslidemenu.view.SlideMenu
android:id="@+id/mSmenu"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
</com.amqr.diyslidemenu.view.SlideMenu>
</RelativeLayout>
.
.
二、弄兩個(gè)布局文件虫蝶,一個(gè)左側(cè)菜單的章咧,一個(gè)是主頁(yè)部分的。SlideView里面把這兩個(gè)布局加載出來(lái)
.
1能真、準(zhǔn)備兩個(gè)布局文件赁严,左側(cè)菜單的布局文件需要控制寬度,這里我們?cè)O(shè)置為200dp
左側(cè)菜單 slide_left.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="match_parent"
android:background="#ff0000"
>
<!--左側(cè)的菜單限定為200dp-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="本地"
android:textSize="26sp"
android:gravity="center"
android:layout_marginTop="10dp"
android:background="#689342"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="體育"
android:textSize="26sp"
android:gravity="center"
android:layout_marginTop="10dp"
android:background="#689342"
/>
</LinearLayout>
</ScrollView>
主頁(yè)部分 slide_main.xml
<?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="#ffffff"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="主頁(yè)內(nèi)容區(qū)域"
android:layout_gravity="center"
android:textSize="30dp"
android:layout_centerInParent="true"
/>
</RelativeLayout>
2舟陆、引用xml的布局代碼里面include進(jìn)來(lái)左側(cè)菜單和布局文件
這個(gè)include的先后順序需要嚴(yán)格區(qū)分
因?yàn)榇龝?huì)需要結(jié)合SlideView的onFinishInflate相互結(jié)合
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.amqr.diyslidemenu.MainActivity">
<com.amqr.diyslidemenu.view.SlideMenu
android:id="@+id/mSmenu"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<!--這個(gè)include的先后順序需要嚴(yán)格區(qū)分误澳,
因?yàn)榇龝?huì)需要結(jié)合SlideView的onFinishInflate相互結(jié)合 -->
<!--左側(cè)菜單-->
<include layout="@layout/slide_left"/>
<!--主頁(yè)部分-->
<include layout="@layout/slide_main"/>
</com.amqr.diyslidemenu.view.SlideMenu>
</RelativeLayout>
三耻矮、利用SlideView的onFinishInflate方法加載view
利用SlideView的onFinishInflate方法加載view
public class SlideMenu extends ViewGroup{
private View mLeftMenu;
private View mMainPage;
public SlideMenu(Context context) {
super(context);
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 當(dāng)SlideView被xml引用加載之后完成秦躯,這個(gè)方法就會(huì)調(diào)用。
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate(); // 裆装?踱承??
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
}
}
看一下onFinishInflate這個(gè)方法哨免,屬于View類下一個(gè)方法
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*
* <p>Even if the subclass overrides onFinishInflate, they should always be
* sure to call the super method, so that we get called.
*/
@CallSuper
protected void onFinishInflate() {
}
getChildAt的作用是 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
@Override
protected void onFinishInflate() {
super.onFinishInflate(); // 茎活??琢唾?
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
}
四载荔、利用onMeasure來(lái)孩子測(cè)量大小
首先記住,不管干嘛采桃,首先先把現(xiàn)在onMeasure里面把setMeasuredDimension方法給寫(xiě)上懒熙。
說(shuō)在測(cè)量之前的第一點(diǎn)
我們的ViewGroup也是View這點(diǎn)我們都知道,其實(shí)到最終普办,ViewGroup到最后還是給他的父親調(diào)用工扎,他的父親就是使用使用measure來(lái)測(cè)量ViewGroup的大小的。說(shuō)了這么多衔蹲,我們還是看一下代碼吧肢娘。
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.amqr.diyslidemenu.MainActivity">
<!--這個(gè)ViewGroup,他的父親就是上面的RelativeLayout舆驶,RelativeLayout就是使用measure來(lái)測(cè)量這個(gè)SlideView的大小的-->
<com.amqr.diyslidemenu.view.SlideMenu
android:id="@+id/mSmenu"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<!--這個(gè)include的先后順序需要嚴(yán)格區(qū)分橱健,
因?yàn)榇龝?huì)需要結(jié)合SlideView的onFinishInflate相互結(jié)合 -->
<!--左側(cè)菜單-->
<include layout="@layout/slide_left"/>
<!--主頁(yè)部分-->
<include layout="@layout/slide_main"/>
</com.amqr.diyslidemenu.view.SlideMenu>
</RelativeLayout>
這個(gè)SlideMenu是ViewGroup,他的父親就是上面的RelativeLayout沙廉,RelativeLayout就是使用measure來(lái)測(cè)量這個(gè)SlideView的大小的
明白了onMeasure是給measure調(diào)用的之后畴博,我們就應(yīng)該清楚地知道,onMeasure是父親給孩子用的寬高(父親把自己所能給的都給了蓝仲,也就是最大的俱病,我們可以采用父親的寬高官疲,我們可以自己指定寬高。但是孩子的自由發(fā)揮的空間沒(méi)有辦法超出父親所能給的最大值亮隙,但是可以比父親型举臁)
說(shuō)在測(cè)量之前的第二點(diǎn)
怎么得到一個(gè)View在xml布局文件里面寬?
利用view.getLayoutParams().width,高類似
有了前面的兩點(diǎn)說(shuō)明溢吻,現(xiàn)在我們可以真正式在SlideMenu里面復(fù)寫(xiě)onMeasere方法并且進(jìn)行測(cè)量了
代碼如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// makeMeasureSpec 的時(shí)候维费,第一個(gè)參數(shù)是大小,第二個(gè)參數(shù)是模式
// 寬度我們使用左側(cè)菜單的在xml里面的200dp促王,因?yàn)橹付ù笮∷阅J绞荕easureSpec.EXACTLY
// 怎么得到一個(gè)View在xml布局文件里面寬,利用view.getLayoutParams().width,高類似
int leftViewMeasureSpecWidth = MeasureSpec.
makeMeasureSpec(mLeftMenu.getLayoutParams().width, MeasureSpec.EXACTLY);
//左側(cè)菜單的的高度我們希望填充父窗體颤专,而當(dāng)前onMeasure里面的heightMeasureSpec根據(jù)我們的布局顯然就是填充父窗體
// 所以一直接用父親傳過(guò)來(lái)的這個(gè)32位參數(shù)就好
mLeftMenu.measure(leftViewMeasureSpecWidth,heightMeasureSpec);
// 至于主頁(yè)頁(yè)面,我們的希望他寬高都是填充父窗體簇捍,所以直接用onMeasure里面?zhèn)鬟^(guò)來(lái)的參數(shù)就好啦
mMainPage.measure(widthMeasureSpec,heightMeasureSpec);
// onMeasure一開(kāi)始什么都不管就應(yīng)該復(fù)寫(xiě)setMeasuredDimension,不然報(bào)錯(cuò)。
// 除非我們的自定義控件是寬高都是填充父窗體么鹤,那么我們就留著下面這句super的代碼就可以
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
關(guān)于測(cè)量的View的measure方法的參數(shù)可以參考(初識(shí)自定義控件)
五、利用onLayout給自孩子擺放位置
在onMeasure里面給孩子布局采用的方法是 child.measure方法
利用onLayout給自己孩子擺放位置相應(yīng)來(lái)說(shuō)用的是 child.layout方法
擺放之前,了解getMeasuredWidth();和getWidth()的區(qū)別
我們下面說(shuō)的layout的前提是已經(jīng)進(jìn)行onMeasure被執(zhí)行之后(onMeasure里面必須執(zhí)行setMeasuredDimension)
getWidth()必須在控件的 layout(l,t,r,b) 被執(zhí)行過(guò)后才能獲取到有效的值瓤荔,也就在View被繪制好之后才有效
getMeasuredWidth(); 是 layout(l,t,r,b)執(zhí)行之前就會(huì)獲取到View的寬度的腔丧,也就是在View被繪制好之前就可以生效的衣厘。
先測(cè)量型宙,后擺放搁嗓。
也就是 先onMeasure棍矛,后onLayout慨绳。
有了這些了解战秋,我們可以來(lái)很好地?cái)[放位置了
代碼如下
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftViewWidth = mLeftMenu.getMeasuredWidth();
int leftViewHeight = mLeftMenu.getMeasuredHeight();
// l t r b 左上右下 左上一點(diǎn)疯搅,右下一點(diǎn)礁蔗,兩點(diǎn)確定了一個(gè)矩形的大小
mLeftMenu.layout(-leftViewWidth,0,0,leftViewHeight);
int mainViewWidth = mMainPage.getMeasuredWidth();
int mainViewHeight = mMainPage.getMeasuredHeight();
mMainPage.layout(0,0,mainViewWidth,mainViewHeight);
}
到此為止先停一下,看一下SlideView里面目前的代碼:
SlideMenu
public class SlideMenu extends ViewGroup{
private View mLeftMenu;
private View mMainPage;
public SlideMenu(Context context) {
super(context);
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 當(dāng)SlideView被xml引用加載之后完成呵曹,這個(gè)方法就會(huì)調(diào)用。
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate(); // 民假?宰衙?考余?
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// makeMeasureSpec 的時(shí)候,第一個(gè)參數(shù)是大小酥筝,第二個(gè)參數(shù)是模式
// 寬度我們使用左側(cè)菜單的在xml里面的200dp滚躯,因?yàn)橹付ù笮∷阅J绞荕easureSpec.EXACTLY
// 怎么得到一個(gè)View在xml布局文件里面寬,利用view.getLayoutParams().width,高類似
int leftViewMeasureSpecWidth = MeasureSpec.
makeMeasureSpec(mLeftMenu.getLayoutParams().width, MeasureSpec.EXACTLY);
//左側(cè)菜單的的高度我們希望填充父窗體,而當(dāng)前onMeasure里面的heightMeasureSpec根據(jù)我們的布局顯然就是填充父窗體
// 所以一直接用父親傳過(guò)來(lái)的這個(gè)32位參數(shù)就好
mLeftMenu.measure(leftViewMeasureSpecWidth,heightMeasureSpec);
// 至于主頁(yè)頁(yè)面,我們的希望他寬高都是填充父窗體掸掏,所以直接用onMeasure里面?zhèn)鬟^(guò)來(lái)的參數(shù)就好啦
mMainPage.measure(widthMeasureSpec,heightMeasureSpec);
// onMeasure一開(kāi)始什么都不管就應(yīng)該復(fù)寫(xiě)setMeasuredDimension茁影,不然報(bào)錯(cuò)。
// 除非我們的自定義控件是寬高都是填充父窗體丧凤,那么我們就留著下面這句super的代碼就可以
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftViewWidth = mLeftMenu.getMeasuredWidth();
int leftViewHeight = mLeftMenu.getMeasuredHeight();
Log.d("Slide","getWidth:"+mLeftMenu.getWidth()+" mLeftMenu.getHeight():"+mLeftMenu.getHeight());
Log.d("Slide","getMeasuredWidth:"+mLeftMenu.getMeasuredWidth()+" getMeasuredHeight:"+mLeftMenu.getMeasuredHeight());
// l t r b 左上右下 左上一點(diǎn)募闲,右下一點(diǎn),兩點(diǎn)確定了一個(gè)矩形的大小
mLeftMenu.layout(-leftViewWidth,0,0,leftViewHeight);
int mainViewWidth = mMainPage.getMeasuredWidth();
int mainViewHeight = mMainPage.getMeasuredHeight();
mMainPage.layout(0, 0, mainViewWidth, mainViewHeight);
}
}
MainActivity
package com.amqr.diyslidemenu;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
運(yùn)行效果:
當(dāng)前還無(wú)法拉動(dòng)愿待,但是我們已經(jīng)過(guò)能讓界面顯示出來(lái)了浩螺。
六、開(kāi)始做移動(dòng)效果
移動(dòng)的方式有很多種仍侥,這次我們采用的是ScrollTo+ScrollBy方式
說(shuō)明1: ScrollTo和ScrollBy 的了解
ScrollTo和ScrollBy就是手機(jī)屏幕的左上角動(dòng)年扩,而不是View或者ViewGroup動(dòng)。
區(qū)別是访圃,ScrollTo每次都是都是想比較于最開(kāi)始的左上角(0,0)
ScrollBy每次的移動(dòng)是累計(jì)的
比如厨幻,調(diào)用ScollTo(20,0),的時(shí)候矛市,那么手機(jī)屏幕回向右移動(dòng)20個(gè)單位悟衩,但是再次調(diào)用ScollTo(20,0),的時(shí)候是不動(dòng)的怠蹂,因?yàn)槊看味际歉铋_(kāi)始的(0剑梳,0)做比較节预;然后我們調(diào)用ScrollTo(-20,0)的時(shí)候赡艰,就回到最開(kāi)的原點(diǎn)棉胀。
ScrollBy是累計(jì)的挤巡,第一次調(diào)用ScrollBy(20,0),向右移動(dòng)20 個(gè)單位徽鼎,再次調(diào)用ScrollBy(20,0)盛末,那么屏幕的左上角就會(huì)移動(dòng)到(40,0)的位置,因?yàn)槔塾?jì)嘛否淤。
說(shuō)明2: getX和getRawX的了解
getX()是觸摸的點(diǎn)與控件自身的距離
getRawX()是觸摸的點(diǎn)與屏幕的距離
結(jié)論:當(dāng)你觸到按鈕時(shí)悄但,x,y是相對(duì)于該按鈕左上點(diǎn)(控件本身)的相對(duì)位置。而rawx,rawy始終是相對(duì)于屏幕的位置石抡。
說(shuō)明3:getScrollX()和getScrollY()的了解
getScrollX(): 手機(jī)屏幕顯示區(qū)域左上角 與 你指定的View的左上角的橫向距離getScrollY(): 手機(jī)屏幕顯示區(qū)域左上角 與 你指定的View的左上角的垂直距離(因?yàn)樽右晥D的高度和手機(jī)屏幕高度一樣)
六.1檐嚣、簡(jiǎn)單的移動(dòng),不會(huì)產(chǎn)生越界現(xiàn)象
public class SlideMenu extends ViewGroup{
private View mLeftMenu;
private View mMainPage;
private int downX;
public SlideMenu(Context context) {
super(context);
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 當(dāng)SlideView被xml引用加載之后完成啰扛,這個(gè)方法就會(huì)調(diào)用嚎京。
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate(); // ?隐解?鞍帝?
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// makeMeasureSpec 的時(shí)候,第一個(gè)參數(shù)是大小煞茫,第二個(gè)參數(shù)是模式
// 寬度我們使用左側(cè)菜單的在xml里面的200dp帕涌,因?yàn)橹付ù笮∷阅J绞荕easureSpec.EXACTLY
// 怎么得到一個(gè)View在xml布局文件里面寬岩臣,利用view.getLayoutParams().width,高類似
int leftViewMeasureSpecWidth = MeasureSpec.
makeMeasureSpec(mLeftMenu.getLayoutParams().width, MeasureSpec.EXACTLY);
//左側(cè)菜單的的高度我們希望填充父窗體,而當(dāng)前onMeasure里面的heightMeasureSpec根據(jù)我們的布局顯然就是填充父窗體
// 所以一直接用父親傳過(guò)來(lái)的這個(gè)32位參數(shù)就好
mLeftMenu.measure(leftViewMeasureSpecWidth,heightMeasureSpec);
// 至于主頁(yè)頁(yè)面,我們的希望他寬高都是填充父窗體宵膨,所以直接用onMeasure里面?zhèn)鬟^(guò)來(lái)的參數(shù)就好啦
mMainPage.measure(widthMeasureSpec,heightMeasureSpec);
// onMeasure一開(kāi)始什么都不管就應(yīng)該復(fù)寫(xiě)setMeasuredDimension架谎,不然報(bào)錯(cuò)。
// 除非我們的自定義控件是寬高都是填充父窗體辟躏,那么我們就留著下面這句super的代碼就可以
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftViewWidth = mLeftMenu.getMeasuredWidth();
int leftViewHeight = mLeftMenu.getMeasuredHeight();
Log.d("Slide","getWidth:"+mLeftMenu.getWidth()+" mLeftMenu.getHeight():"+mLeftMenu.getHeight());
Log.d("Slide","getMeasuredWidth:"+mLeftMenu.getMeasuredWidth()+" getMeasuredHeight:"+mLeftMenu.getMeasuredHeight());
// l t r b 左上右下 左上一點(diǎn)谷扣,右下一點(diǎn),兩點(diǎn)確定了一個(gè)矩形的大小
mLeftMenu.layout(-leftViewWidth,0,0,leftViewHeight);
int mainViewWidth = mMainPage.getMeasuredWidth();
int mainViewHeight = mMainPage.getMeasuredHeight();
mMainPage.layout(0, 0, mainViewWidth, mainViewHeight);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//return super.onTouchEvent(event);
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
downX = (int)(event.getX()+0.5f);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int)(event.getX()+0.5f);
// 用減法捎琐,比如按下是0会涎,最終移動(dòng)到了20,那么屏幕向左邊移動(dòng)20個(gè)單位瑞凑,左側(cè)菜單就可顯示出來(lái)了
int distanceX = downX - moveX;
int scrollX = getScrollX(); // 注意getScaleX()不要寫(xiě)成getScaleX()
// 解決越界問(wèn)題
if(scrollX+distanceX < (-mLeftMenu.getMeasuredWidth())){ // 左側(cè)越界臨界點(diǎn)
scrollTo(-mLeftMenu.getMeasuredWidth(),0);
}else if(scrollX+distanceX>0){ // 右側(cè)越界臨界點(diǎn)
scrollTo(0,0);
}else{ // 在兩個(gè)臨界點(diǎn)之間的可移動(dòng)范圍
scrollBy(distanceX,0);
}
downX = moveX;
break;
case MotionEvent.ACTION_UP:
break;
}
//關(guān)鍵一步末秃,返回true,代表消費(fèi)當(dāng)前的偷吃事件
return true;
}
}
六.2练慕、判斷松手后應(yīng)該停留在哪一個(gè)界面
做的事情很簡(jiǎn)單,其實(shí)也就是在 case MotionEvent.ACTION_UP: 里面添加幾行代碼
case MotionEvent.ACTION_UP:
int upScrollX = getScrollX();
if(upScrollX<-(mLeftMenu.getMeasuredWidth()/2)){
scrollTo(-mLeftMenu.getMeasuredWidth(),0);
}else{
scrollTo(0,0);
}
break;
六.3技掏、使用Scoller彈性滑動(dòng)铃将,讓滑動(dòng)產(chǎn)生過(guò)渡效果
自定義View做動(dòng)畫(huà)有很多做法,Scroller是其中一種哑梳,也是我們這次要采用的做法劲阎。
Scoller彈性滑動(dòng)是使用過(guò)程:
1、實(shí)例化Scroller
實(shí)例化一個(gè)Scroller
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
scroller = new Scroller(getContext()); // 實(shí)例化一個(gè)Scroll鸠真,需要context
}
2悯仙、調(diào)用startScroll(startX, startY, dx, dy, durationTime);模擬數(shù)據(jù)變化,接著調(diào)用invalidate(); 觸發(fā)computeScroll()
注意點(diǎn):startScroll 只是模擬數(shù)據(jù)的變化吠卷,想要看到效果還需要調(diào)用invalidate重新刷新UI锡垄,其實(shí)就是調(diào)用onDraw
注意點(diǎn):invalidate();經(jīng)過(guò)輾轉(zhuǎn)會(huì)去computeScroll();
invalidate(); ---> draw()-->onDraw()--> computeScroll();
3、在computeScroll()里面復(fù)寫(xiě)真正讓讓模擬數(shù)據(jù)生效的代碼,調(diào)用invalidate()
注意點(diǎn):
scroller.computeScrollOffset()
computeScrollOffset()為true代表模擬數(shù)據(jù)還沒(méi)有完成
注意點(diǎn):scroller.getCurrX()當(dāng)前時(shí)刻正模擬到的數(shù)據(jù)
代碼如下(其實(shí)這幾乎是模板代碼):
@Override
public void computeScroll() { // 如果數(shù)據(jù)模擬沒(méi)有完成撤嫩,那么繼續(xù)更新
//super.computeScroll();
// computeScrollOffset
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),0);
invalidate(); // 注意這里還調(diào)用了invalidate偎捎,這樣才會(huì)產(chǎn)生效果
}
}
.
.
上面幾點(diǎn)已經(jīng)說(shuō)完,具體看看在代碼中怎么使用吧
computeScroll();和computeScrollOffset()的結(jié)合使用
case MotionEvent.ACTION_UP:
int upScrollX = getScrollX();
choosePage(upScrollX<-(mLeftMenu.getMeasuredWidth()/2));
break;
}
//關(guān)鍵一步序攘,返回true,代表消費(fèi)當(dāng)前的偷吃事件
return true;
}
private void choosePage(boolean isMainPage){
if(isMainPage){
startScrollNow(-mLeftMenu.getMeasuredWidth());
//scrollTo(0,0); 可以實(shí)現(xiàn)但是無(wú)動(dòng)畫(huà)過(guò)渡
}else{
startScrollNow(0);
//scrollTo(-mLeftMenu.getMeasuredWidth(),0); 可以實(shí)現(xiàn)但是無(wú)動(dòng)畫(huà)過(guò)渡
}
}
private void startScrollNow(int endX){
int startX = getScrollX(); // 起始X
int startY = 0; // 起始Y
int dx = endX - startX; // X方向的增量值寻拂,可以理解為距離
int dy = 0; // Y方向的增量值程奠,可以理解為距離
int time = Math.abs(dx) * 10;
int durationTime = (time>600)?600:time;
//startScroll(int startX, int startY, int dx, int dy, int duration)
// 注意: startScroll 只是模擬數(shù)據(jù)的變化,想要看到效果還需要調(diào)用invalidate重新刷新UI祭钉,其實(shí)就是調(diào)用onDraw
scroller.startScroll(startX, startY, dx, dy, durationTime);
invalidate(); // 關(guān)鍵代碼瞄沙,invalidate和computeScroll才會(huì)有動(dòng)畫(huà)效果,scroller.startScroll只是模擬數(shù)據(jù)
}
@Override
public void computeScroll() { // 如果數(shù)據(jù)模擬沒(méi)有完成,那么繼續(xù)更新
//super.computeScroll();
// computeScrollOffset
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),0);
invalidate(); // 注意這里還調(diào)用了invalidate,這樣才會(huì)產(chǎn)生效果
}
}
六距境、4申尼、添加是 否處于主頁(yè)的方法 和 展示那個(gè)頁(yè)面的方法
其實(shí)也就是添加了isAtMainPage、showMainPage()和showLeftPage()這三個(gè)方法
public class SlideMenu extends ViewGroup{
private static final int MAIN_PAGE = 0;
private static final int LEFT_PAGE = 1;
private View mLeftMenu;
private View mMainPage;
private int downX;
private Scroller scroller;
private int pageIndex;
public SlideMenu(Context context) {
super(context);
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 當(dāng)SlideView被xml引用加載之后完成垫桂,這個(gè)方法就會(huì)調(diào)用师幕。
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate(); // ?诬滩?霹粥?
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
scroller = new Scroller(getContext()); // 實(shí)例化一個(gè)Scroll,需要context
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// makeMeasureSpec 的時(shí)候疼鸟,第一個(gè)參數(shù)是大小后控,第二個(gè)參數(shù)是模式
// 寬度我們使用左側(cè)菜單的在xml里面的200dp,因?yàn)橹付ù笮∷阅J绞荕easureSpec.EXACTLY
// 怎么得到一個(gè)View在xml布局文件里面寬空镜,利用view.getLayoutParams().width,高類似
int leftViewMeasureSpecWidth = MeasureSpec.
makeMeasureSpec(mLeftMenu.getLayoutParams().width, MeasureSpec.EXACTLY);
//左側(cè)菜單的的高度我們希望填充父窗體浩淘,而當(dāng)前onMeasure里面的heightMeasureSpec根據(jù)我們的布局顯然就是填充父窗體
// 所以一直接用父親傳過(guò)來(lái)的這個(gè)32位參數(shù)就好
mLeftMenu.measure(leftViewMeasureSpecWidth,heightMeasureSpec);
// 至于主頁(yè)頁(yè)面,我們的希望他寬高都是填充父窗體,所以直接用onMeasure里面?zhèn)鬟^(guò)來(lái)的參數(shù)就好啦
mMainPage.measure(widthMeasureSpec, heightMeasureSpec);
// onMeasure一開(kāi)始什么都不管就應(yīng)該復(fù)寫(xiě)setMeasuredDimension吴攒,不然報(bào)錯(cuò)馋袜。
// 除非我們的自定義控件是寬高都是填充父窗體,那么我們就留著下面這句super的代碼就可以
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftViewWidth = mLeftMenu.getMeasuredWidth();
int leftViewHeight = mLeftMenu.getMeasuredHeight();
Log.d("Slide", "getWidth:" + mLeftMenu.getWidth() + " mLeftMenu.getHeight():" + mLeftMenu.getHeight());
Log.d("Slide", "getMeasuredWidth:" + mLeftMenu.getMeasuredWidth() + " getMeasuredHeight:" + mLeftMenu.getMeasuredHeight());
// l t r b 左上右下 左上一點(diǎn)舶斧,右下一點(diǎn)欣鳖,兩點(diǎn)確定了一個(gè)矩形的大小
mLeftMenu.layout(-leftViewWidth,0,0,leftViewHeight);
int mainViewWidth = mMainPage.getMeasuredWidth();
int mainViewHeight = mMainPage.getMeasuredHeight();
mMainPage.layout(0, 0, mainViewWidth, mainViewHeight);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//return super.onTouchEvent(event);
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
downX = (int)(event.getX()+0.5f);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int)(event.getX()+0.5f);
// 用減法,比如按下是0茴厉,最終移動(dòng)到了20泽台,那么屏幕向左邊移動(dòng)20個(gè)單位,左側(cè)菜單就可顯示出來(lái)了
int distanceX = downX - moveX;
int scrollX = getScrollX(); // 注意getScaleX()不要寫(xiě)成getScaleX()
// 解決越界問(wèn)題
if(scrollX+distanceX < (-mLeftMenu.getMeasuredWidth())){ // 左側(cè)越界臨界點(diǎn)
scrollTo(-mLeftMenu.getMeasuredWidth(),0);
}else if(scrollX+distanceX>0){ // 右側(cè)越界臨界點(diǎn)
scrollTo(0,0);
}else{ // 在兩個(gè)臨界點(diǎn)之間的可移動(dòng)范圍
scrollBy(distanceX,0);
}
downX = moveX;
break;
case MotionEvent.ACTION_UP:
int upScrollX = getScrollX();
choosePage(upScrollX<-(mLeftMenu.getMeasuredWidth()/2));
break;
}
//關(guān)鍵一步矾缓,返回true怀酷,代表消費(fèi)當(dāng)前的touch事件
return true;
}
private void choosePage(boolean isMainPage){
if(isMainPage){
pageIndex = MAIN_PAGE;
startScrollNow(-mLeftMenu.getMeasuredWidth());
//scrollTo(0,0); 可以實(shí)現(xiàn)但是無(wú)動(dòng)畫(huà)過(guò)渡
}else{
pageIndex = LEFT_PAGE;
startScrollNow(0);
//scrollTo(-mLeftMenu.getMeasuredWidth(),0); 可以實(shí)現(xiàn)但是無(wú)動(dòng)畫(huà)過(guò)渡
}
}
private void startScrollNow(int endX){
int startX = getScrollX(); // 起始X
int startY = 0; // 起始Y
int dx = endX - startX; // X方向的增量值,可以理解為距離
int dy = 0; // Y方向的增量值嗜闻,可以理解為距離
int time = Math.abs(dx) * 10;
int durationTime = (time>600)?600:time;
//startScroll(int startX, int startY, int dx, int dy, int duration)
// 注意: startScroll 只是模擬數(shù)據(jù)的變化蜕依,想要看到效果還需要調(diào)用invalidate重新刷新UI,其實(shí)就是調(diào)用onDraw
scroller.startScroll(startX, startY, dx, dy, durationTime);
invalidate(); // 關(guān)鍵代碼琉雳,invalidate和computeScroll才會(huì)有動(dòng)畫(huà)效果,scroller.startScroll只是模擬數(shù)據(jù)
}
@Override
public void computeScroll() { // 如果數(shù)據(jù)模擬沒(méi)有完成样眠,那么繼續(xù)更新
//super.computeScroll();
// computeScrollOffset
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),0);
invalidate(); // 注意這里還調(diào)用了invalidate,這樣才會(huì)產(chǎn)生效果
}
}
public boolean isAtMainPage(){
return (pageIndex == MAIN_PAGE)?true:false;
}
public void showMainPage(){
choosePage(true);
}
public void showLeftPage(){
choosePage(false);
}
}
六翠肘、5檐束、我們發(fā)現(xiàn)當(dāng)我們滑動(dòng)左側(cè)菜單的時(shí)候,無(wú)法拉動(dòng)菜單束倍。
這就涉及到一個(gè)View的傳遞機(jī)制了被丧。
關(guān)于View的傳遞機(jī)制盟戏,可以參考文章 ——————————
為什么拉動(dòng)左側(cè)的菜單無(wú)法拖動(dòng),這肯定是屬于SlideMenu的左側(cè)菜單在拉動(dòng)的時(shí)候甥桂,SlideMenu的onTouchEvent沒(méi)有被執(zhí)行柿究,為了確保我們的SlideMenu不管是在左側(cè)菜單還是主頁(yè)菜單的都能夠順利拖動(dòng),我們就在SlideMenu里面復(fù)寫(xiě) onInterceptTouchEvent 方法黄选,然后判斷一下蝇摸,如果是橫向滑動(dòng),就攔截下來(lái)糕簿,自己就消費(fèi)掉這個(gè)touch
其實(shí)也就只是添加這么一小段代碼
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
interDownX = (int)(ev.getX()+0.5f);
interDownY = (int)(ev.getY()+0.5f);
break;
case MotionEvent.ACTION_MOVE:
interMoveX = (int)(ev.getX()+0.5f);
interMoveY = (int)(ev.getY()+0.5f);
int diatanceInterX = Math.abs(interMoveX - interDownX);
int distanceInterY = Math.abs(interMoveY - interDownY);
if(diatanceInterX > distanceInterY){ // 代表是水平滑動(dòng)探入,(水平滑動(dòng)的距離比垂直滑動(dòng)的距離大)
return true; // 攔截之后就肯定執(zhí)行onTouchEvent
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev); // 如果不是水平滑動(dòng)就不攔截
}
好啦,該做的差不多都做啦懂诗。
最后附上所有代碼:
SlideMenu完整代碼
public class SlideMenu extends ViewGroup{
private static final int MAIN_PAGE = 0;
private static final int LEFT_PAGE = 1;
private View mLeftMenu;
private View mMainPage;
private int downX;
private Scroller scroller;
private int pageIndex;
private int interDownX;
private int interDownY;
private int interMoveX;
private int interMoveY;
public SlideMenu(Context context) {
super(context);
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 當(dāng)SlideView被xml引用加載之后完成蜂嗽,這個(gè)方法就會(huì)調(diào)用。
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate(); // 殃恒?植旧??
// getChildAt 作用 Returns the view at the specified position in the group.
// 精確地返回在ViewGroup里面的View的位置 所以我們的include順序很重要
mLeftMenu = getChildAt(0); // 左側(cè)菜單
mMainPage = getChildAt(1);
scroller = new Scroller(getContext()); // 實(shí)例化一個(gè)Scroll离唐,需要context
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// makeMeasureSpec 的時(shí)候病附,第一個(gè)參數(shù)是大小,第二個(gè)參數(shù)是模式
// 寬度我們使用左側(cè)菜單的在xml里面的200dp亥鬓,因?yàn)橹付ù笮∷阅J绞荕easureSpec.EXACTLY
// 怎么得到一個(gè)View在xml布局文件里面寬完沪,利用view.getLayoutParams().width,高類似
int leftViewMeasureSpecWidth = MeasureSpec.
makeMeasureSpec(mLeftMenu.getLayoutParams().width, MeasureSpec.EXACTLY);
//左側(cè)菜單的的高度我們希望填充父窗體,而當(dāng)前onMeasure里面的heightMeasureSpec根據(jù)我們的布局顯然就是填充父窗體
// 所以一直接用父親傳過(guò)來(lái)的這個(gè)32位參數(shù)就好
mLeftMenu.measure(leftViewMeasureSpecWidth,heightMeasureSpec);
// 至于主頁(yè)頁(yè)面,我們的希望他寬高都是填充父窗體嵌戈,所以直接用onMeasure里面?zhèn)鬟^(guò)來(lái)的參數(shù)就好啦
mMainPage.measure(widthMeasureSpec, heightMeasureSpec);
// onMeasure一開(kāi)始什么都不管就應(yīng)該復(fù)寫(xiě)setMeasuredDimension覆积,不然報(bào)錯(cuò)。
// 除非我們的自定義控件是寬高都是填充父窗體熟呛,那么我們就留著下面這句super的代碼就可以
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftViewWidth = mLeftMenu.getMeasuredWidth();
int leftViewHeight = mLeftMenu.getMeasuredHeight();
Log.d("Slide", "getWidth:" + mLeftMenu.getWidth() + " mLeftMenu.getHeight():" + mLeftMenu.getHeight());
Log.d("Slide", "getMeasuredWidth:" + mLeftMenu.getMeasuredWidth() + " getMeasuredHeight:" + mLeftMenu.getMeasuredHeight());
// l t r b 左上右下 左上一點(diǎn)宽档,右下一點(diǎn),兩點(diǎn)確定了一個(gè)矩形的大小
mLeftMenu.layout(-leftViewWidth,0,0,leftViewHeight);
int mainViewWidth = mMainPage.getMeasuredWidth();
int mainViewHeight = mMainPage.getMeasuredHeight();
mMainPage.layout(0, 0, mainViewWidth, mainViewHeight);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
interDownX = (int)(ev.getX()+0.5f);
interDownY = (int)(ev.getY()+0.5f);
break;
case MotionEvent.ACTION_MOVE:
interMoveX = (int)(ev.getX()+0.5f);
interMoveY = (int)(ev.getY()+0.5f);
int diatanceInterX = Math.abs(interMoveX - interDownX);
int distanceInterY = Math.abs(interMoveY - interDownY);
if(diatanceInterX > distanceInterY){ // 代表是水平滑動(dòng)庵朝,(水平滑動(dòng)的距離比垂直滑動(dòng)的距離大)
return true; // 攔截之后就肯定執(zhí)行onTouchEvent
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev); // 如果不是水平滑動(dòng)就不攔截
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//return super.onTouchEvent(event);
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
downX = (int)(event.getX()+0.5f);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int)(event.getX()+0.5f);
// 用減法吗冤,比如按下是0,最終移動(dòng)到了20九府,那么屏幕向左邊移動(dòng)20個(gè)單位椎瘟,左側(cè)菜單就可顯示出來(lái)了
int distanceX = downX - moveX;
int scrollX = getScrollX(); // 注意getScaleX()不要寫(xiě)成getScaleX()
// 解決越界問(wèn)題
if(scrollX+distanceX < (-mLeftMenu.getMeasuredWidth())){ // 左側(cè)越界臨界點(diǎn)
scrollTo(-mLeftMenu.getMeasuredWidth(),0);
}else if(scrollX+distanceX>0){ // 右側(cè)越界臨界點(diǎn)
scrollTo(0,0);
}else{ // 在兩個(gè)臨界點(diǎn)之間的可移動(dòng)范圍
scrollBy(distanceX,0);
}
downX = moveX;
break;
case MotionEvent.ACTION_UP:
int upScrollX = getScrollX();
choosePage(upScrollX<-(mLeftMenu.getMeasuredWidth()/2));
break;
}
//關(guān)鍵一步,返回true昔逗,代表消費(fèi)當(dāng)前的touch事件
return true;
}
private void choosePage(boolean isMainPage){
if(isMainPage){
pageIndex = MAIN_PAGE;
startScrollNow(-mLeftMenu.getMeasuredWidth());
//scrollTo(0,0); 可以實(shí)現(xiàn)但是無(wú)動(dòng)畫(huà)過(guò)渡
}else{
pageIndex = LEFT_PAGE;
startScrollNow(0);
//scrollTo(-mLeftMenu.getMeasuredWidth(),0); 可以實(shí)現(xiàn)但是無(wú)動(dòng)畫(huà)過(guò)渡
}
}
private void startScrollNow(int endX){
int startX = getScrollX(); // 起始X
int startY = 0; // 起始Y
int dx = endX - startX; // X方向的增量值降传,可以理解為距離
int dy = 0; // Y方向的增量值,可以理解為距離
int time = Math.abs(dx) * 10;
int durationTime = (time>600)?600:time;
//startScroll(int startX, int startY, int dx, int dy, int duration)
// 注意: startScroll 只是模擬數(shù)據(jù)的變化勾怒,想要看到效果還需要調(diào)用invalidate重新刷新UI婆排,其實(shí)就是調(diào)用onDraw
scroller.startScroll(startX, startY, dx, dy, durationTime);
invalidate(); // 關(guān)鍵代碼,invalidate和computeScroll才會(huì)有動(dòng)畫(huà)效果,scroller.startScroll只是模擬數(shù)據(jù)
}
@Override
public void computeScroll() { // 如果數(shù)據(jù)模擬沒(méi)有完成笔链,那么繼續(xù)更新
//super.computeScroll();
// computeScrollOffset
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),0);
invalidate(); // 注意這里還調(diào)用了invalidate段只,這樣才會(huì)產(chǎn)生效果
}
}
public boolean isAtMainPage(){
return (pageIndex == MAIN_PAGE)?true:false;
}
public void showMainPage(){
choosePage(true);
}
public void showLeftPage(){
choosePage(false);
}
}
.
.
MainActivity
public class MainActivity extends Activity {
private SlideMenu slideMenu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
slideMenu = (SlideMenu) findViewById(R.id.mSmenu);
findViewById(R.id.mIvBack).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(slideMenu.isAtMainPage()){
slideMenu.showLeftPage();
}else{
slideMenu.showMainPage();
}
}
});
findViewById(R.id.mTvBd).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"本地",Toast.LENGTH_SHORT).show();
if(slideMenu.isAtMainPage()){
slideMenu.showLeftPage();
}else{
slideMenu.showMainPage();
}
}
});
}
}
組合控件仿qq側(cè)滑菜單至此結(jié)束。
在組合控件1—— 設(shè)置框一文中鉴扫,我們將進(jìn)行組合控件的demo編寫(xiě)赞枕。
本篇完。