自定义View以及QQ5.0侧滑菜单实现

自定义View中,有三种方式的自定义View:

1 就是组合控件,利用android自带的控件组合起来实现一个全新的控件

2 继承已经有的控件,重写onMeasure(),ondraw()和onlayout()方法实现一个自定义View

3 继承View,重绘所有的界面,实现一个android没有的全新控件


首先第一种:

组合控件,实现一个titleBar的功能


首先在res文件夹下values文件夹下创建一个xml文件,命名为atts.xml,这个就是我们的自定义属性了

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="topbar">
        <attr name="titletext" format="string"/>
        <attr name="leftbuttontext" format="string"/>
        <attr name="leftbg" format="color|reference"/>
        <attr name="righttext" format="string"/>
        <attr name="rightbg" format="color|reference"/>
        <attr name="bgcolor"    format="color" />
    </declare-styleable>
</resources>
这个里面,declare-styleable就是自定义的控件属性,name为topbar

每一行attr就是一个属性,name为属性的名字,format为属性的格式,有string,color,referce(这个是背景可以设置为图片),还有dimension(尺寸,单位为dp)

自定义控件的属性创造后,因为要实现的是组合控件,所以我选择继承一个ViewGroup类RelativeLayout,可以很好的用来组合控件

继承这个控件首先要实现它的构造方法

public MyRealtilayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}
这里要通过typdearray来获得属性值并与属性值绑定

我们要创建的titlebar布局很简答, 就是左边一个按钮,中间一个textview,右边一个按钮

属性也是按照刚才自定义的属性来设置的

然后,创建一个typedarray通过调用context.obtainStyledAtrributes()方法来获得,这里需要传入两个参数,第一个参数就是构造方法中的attrs,而第二个参数就是我们刚才自定义的那个xml文件

TypedArray td=context.obtainStyledAttributes(attrs,R.styleable.topbar);
这样td中有一个方法就是getXXX()方法,XXX就是你要获得的属性的类型,以刚才定义的xml文件为例子

lefttext=td.getString(R.styleable.topbar_leftbuttontext);
leftcolor=td.getColor(R.styleable.topbar_leftbg, Color.GRAY);
bgcolor=td.getColor(R.styleable.topbar_bgcolor,Color.WHITE);
righttext=td.getString(R.styleable.topbar_righttext);
rightcolor=td.getColor(R.styleable.topbar_rightbg,Color.BLUE);
title=td.getString(R.styleable.topbar_titletext);
td.recycle();
getXXX方法中需要传入的参数就是刚才自定义的属性值,andorid自动为我们命名了,采用name+属性name的方法来命名,而有一些需要传入默认的参数。

最后结尾一定要通过调用recycle方法来释放资源

获得值以后还需要获得实际的控件,这里定义了两个按钮和一个TextView并且获得值

left=new Button(context);
right=new Button(context);
titleview=new TextView(context);
然后通过相应控件的设置来将刚才得到的数值赋给控件

left.setText(lefttext);
left.setBackgroundColor(leftcolor);
right.setText(righttext);
right.setBackgroundColor(rightcolor);
titleview.setText(title);
this.setBackgroundColor(bgcolor);
这里开始添加View

通过LayoutParams这个类可以很好地管理控件的大小和布局

LayoutParams leftp,rightp,titlep;
leftp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rightp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
titlep=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
并且通过调用addRule方法可以设置在父布局中的位置

leftp.addRule(ALIGN_PARENT_LEFT);
rightp.addRule(ALIGN_PARENT_RIGHT);
titlep.addRule(CENTER_IN_PARENT);
最后在通过addView方法添加到控件中。


另外我们还可以通过接口回调的方式来监听左右按钮的点击事件

首先定义一个接口OnTopBarLinsner

里面有两个方法,一个是OnTouchLeft和OnTouchRight方法

public interface onTopBarlinsner{
    public void onTouchleft();
    public void onTouchright();
}
并且我们在类中也定义这个接口

private onTopBarlinsner topBarlinsner;
在添加一个方法用来获得调用者的linsner

public void addLinsner(onTopBarlinsner topBarlinsner){
    this.topBarlinsner=topBarlinsner;
}
这里,在相应的按钮中调用相应的方法,这里因为是接口回调,我们不关心具体是如果实现,但是一定要有这个方法

left.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        topBarlinsner.onTouchleft();
    }
});
right.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        topBarlinsner.onTouchright();
    }
});
通过调用了按钮自己的监听事件,触发了自己的监听事件。


在主布局文件中还有相应的修改

xmlns:mlx="http://schemas.android.com/apk/res-auto"
这里就是导入相应的命名空间,前面的mlx就是自定义的命名空间,在Androidstudio中,直接写res-suto就可以自己找到相对应的布局

然后加上自己写的控件,这里一定要完整地包名,而且其中的mlx属性就是自己定义的属性

<com.example.topbar.MyRealtilayout
    android:id="@+id/my"
    android:layout_width="match_parent"
    mlx:titletext="自定义标题"
    mlx:leftbuttontext="左按钮"
    mlx:righttext="右边按钮"
    mlx:leftbg="#352052"
    mlx:rightbg="#FFF"
    mlx:bgcolor="#ccc"
    android:layout_height="60dp"
    android:layout_alignParentTop="true">
</com.example.topbar.MyRealtilayout>

在MainActivity中定义自己实现的控件,然后调用addLinsner方法传入一个匿名内部类,这里就和普通按钮方式是一样的

realtilayout.addLinsner(new MyRealtilayout.onTopBarlinsner() {
    @Override
    public void onTouchleft() {
        Toast.makeText(MainActivity.this,"你点击了左边按钮",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onTouchright() {
        Toast.makeText(MainActivity.this,"你点击了右边按钮",Toast.LENGTH_SHORT).show();
    }
});
至此就是一个完整地topbar的自定义组合控件。

在这里需要注意的就是接口回调的方法,还有获得自定义属性的方法


总结一下就是首先自定义属性xml文件,然后创建一个类继承一个ViewGroup,在构造方法中获得属性值,并且通过LauoutParams来设置相应的大小和布局



然后实现一个QQ5.0的侧滑


这里可以观察得到 QQ5.0有一个正文内容和一个左菜单内容,因为主要是为了实现侧滑菜单Slindmenu的功能,content就为一个QQ的截图

可以使用水平的滚动条来添加两个界面到一个HorizontalScrollView中,这里要说明的是当HorizontalScrollView显示到主界面后,左上角为0.0原点


这里要实现左边布局,因为比较简单就不做介绍了。介绍一下主布局中的布局,这里我们添加一个HorizontalScrollView

<com.example.qq50.SlindMenu
    mlx:MenuPadding="100dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:layout_height="match_parent"
        android:weightSum="1">
        
        <include layout="@layout/item" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:background="@drawable/qq"
            android:layout_height="match_parent">
        </LinearLayout>
    </LinearLayout>
</com.example.qq50.SlindMenu>

这里已经使用好已经定义好的控件,是继承自HorizontalScrollView的,其中通过include来导入之前的菜单布局这个时候布局已经写完,具体的宽度需要通过代码来设置

创建一个类继承HorizontalScrollView,然后定义一个属性屏幕的宽度

int scrennwidth;
屏幕的宽度可以通过windowsserve服务来获取

WindowManager windowManager= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics=new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);

scrennwidth=metrics.widthPixels;
getMetrics()方法需要传入的是一个out对象来得到屏幕的宽度,这样就可以了。因为要侧滑的时候显示出主界面,这个时候menu不能占据屏幕所有的宽度,所以需要设置一个宽度值来保留一部分主界面。下面这个方法可以吧PX转换为dp

leftpadiding= ta.getDimensionPixelSize(R.styleable.SlindMenu_MenuPadding,
        (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
        50,context.getResources().getDisplayMetrics()));
接着可以在onMeasure方法中设置两个界面的宽度

if(First){
    mwrap= (LinearLayout) getChildAt(0);
    leftmenu= (ViewGroup) mwrap.getChildAt(0);
    content= (ViewGroup) mwrap.getChildAt(1);

    menuWidth=leftmenu.getLayoutParams().width=scrennwidth-leftpadiding;
    content.getLayoutParams().width=scrennwidth;
    First=false;
}
在上面的自定义View中介绍了LayoutParams可以设置具体的宽度和高度,这里设置好了以后,因为刚进入界面要显示的是QQ界面,所以要先把menu隐藏,而这里隐藏可以使用scrollto方法直接滚动到具体的数值地方,这里的滚动是以父布局左上角为原点,也可以理解为让这个数值的地方显示在屏幕的左上角。这里

if(changed){
    this.scrollTo(menuWidth,0);
}
滚动的就是menu的宽度,直接让menu隐藏在左边了。显示的是HorizontalScrollView的menu宽度的这个位置

同时,我们还要求在左边隐藏的宽度小于menu/2的宽度时要直接显示menu,大于ment/2的要隐藏menu,这里监听一下手指的滑动事件,只需要监听一下up事件就可以

int action=ev.getAction();
switch (action){
    case MotionEvent.ACTION_UP:{
        int scrolx=getScrollX();
        if(scrolx>=menuWidth/2){
            //Log.i("ll","ss");
            this.smoothScrollTo(menuWidth,0);
        }else{
            this.smoothScrollTo(0,0);
        }
    }
    return true;
}
调用smoothScrollTo方法是因为这个可以换换滑动过去而不是直接滑动到指定位置。这样,所有的布局就已经写完了


同时,还有一个抽屉式的效果,抽屉式效果的话原理是menu直接设置在屏幕下面,然后每当整个HorizontalScrollView向右滑动100px,menu就得相对HorizontalScrollView向左滑动100px,这里可以调用onscrollchange方法来进行设置

protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    //Log.i("Test","l"+l+",oldl:"+oldl);
    super.onScrollChanged(l, t, oldl, oldt);

    ObjectAnimator animator=ObjectAnimator.ofFloat(leftmenu,"translationX",oldl
            ,l);
    animator.setDuration(0);
    animator.start();
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值