Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android-自定义控件开发 #32

Open
jeffrey1995 opened this issue Aug 14, 2018 · 0 comments
Open

Android-自定义控件开发 #32

jeffrey1995 opened this issue Aug 14, 2018 · 0 comments

Comments

@jeffrey1995
Copy link
Owner

前言

今天总结一下Android开发中的自定义控件的开发,Android中所有控件和布局的基类都是View,自定义控件也就是继承View或者View的派生类,然后再重写类中的内部方法。
通常来说自定义控件分为三种:
1.自定义View:继承View
2.基于现有组件:继承View的派生类
3.组合的方式:自定义控件中包含了其他的组件
下图体现了各种控件的继承关系:
View派生类.png

来实践一下

有一个需求是这样的,需要在页面中构建一种菜单列表,菜单单个项目如图所示:
item.png
当我们构建这样的菜单列表,不必每一个item在布局里都重写一次,可以提取相同的元素,将单个项目封装成控件,需要时直接引用即可。
思路:
分析一下布局中的元素,左边的icon,左边的titleText,右边的hintText,右边的icon,分割线等。抽取出相同的部分,不同的部分通过自定义样式类型来区分,实现最大程度上的复用。我使用第三种组合的方式来编写。

1.定义组件布局:
按需求编写出对应布局,包含了所有元素

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/menu_item_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:orientation="vertical">

    <View
        android:id="@+id/divide_line_view"
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginLeft="49.5dp"
        android:background="@color/divide"
        android:visibility="gone" />

    <View
        android:id="@+id/divide_area_view"
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:background="@color/main_bg"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/ripple_with_mask"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="15dp"
        android:paddingRight="15dp">

        <ImageView
            android:id="@+id/menu_item_icon_img"
            android:layout_width="24dp"
            android:layout_height="24dp" />

        <TextView
            android:id="@+id/menu_item_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10.5dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:textSize="15sp"
            tools:text="标题文字" />

        <TextView
            android:id="@+id/menu_item_text_hint"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/mc_txt_gray_bg"
            android:textSize="13sp"
            android:layout_marginRight="10dp"
            tools:text="提示文字" />

        <ImageView
            android:id="@+id/menu_item_red_hint"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:layout_marginRight="10dp"
            android:src="@drawable/msg_red"
            android:visibility="gone" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/arrow_right" />
    </LinearLayout>

</LinearLayout>

1.1.color.xml:

<!--布局颜色-->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="white">#ffffff</color>
    <color name="divide">#e6ebf0</color>
    <color name="main_bg">#F3F3F3</color>
    <color name="mc_txt_gray_bg">#a6a6a6</color>
    <color name="ripple_color">#19333333</color>
</resources>

1.2.attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MenuItemLayout">
        <attr name="title_text" format="string" />
        <attr name="hint_text" format="string" />
        <attr name="icon_reference" format="reference" />
        <attr name="icon_uri" format="string" />
        <attr name="jump_url" format="string" />
        <attr name="divide_line_style" format="integer" />
    </declare-styleable>
</resources>

1.3.红点图片msg_red.png和箭头图标arrow_right.png

2.继承FrameLayout,复写三个构造函数。
2.1.读取布局文件中的属性参数(见init方法):
如果在布局中传入了自定义的参数,可以在构造函数中从AttributeSet读取并设置给控件。
2.2.在MenuItemLayout中找到了所有的控件并初始化它们,分割线样式设置了三种:白色区域、灰色分割线、以及无样式。onclickId是为了给动态生成的布局设置点击事件,根据属性来控制控件的显示。

package com.example.tianxiying.mytestapplication;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;


/**
 * Created by tianxiying on 2018/3/1.
 */

public class MenuItemLayout extends FrameLayout {
    private Context mContext;
    private View mView;
    private TextView titleTv;
    private TextView hintTv;
    private ImageView redHintImg;
    private ImageView iconImg;
    private OnClickListener onClickListener;
    private String titleText;
    private String hintText;
    private String iconImgUri;
    private String jumpUrl;
    private int iconImgId;
    private String onclickId;
    public static final int NO_LINE = 0;
    public static final int DIVIDE_LINE = 1;
    public static final int DIVIDE_AREA = 2;
    public int divideLineStyle = NO_LINE;
    private boolean isShowRedHintImg = false;

    public int getIconImgId() {
        return iconImgId;
    }

    public void setIconImgId(int iconImgId) {
        if (iconImgId != 10000) {
            this.iconImgId = iconImgId;
            iconImg.setImageResource(iconImgId);
        }
    }

    public String getTitleText() {
        return titleText;
    }

    public void setTitleText(String titleText) {
        if (titleText != null) {
            this.titleText = titleText;
            titleTv.setText(titleText);
        }
    }

    public String getHintText() {
        return hintText;
    }

    public void setHintText(String hintText) {
        if (hintText != null) {
            this.hintText = hintText;
            hintTv.setText(hintText);
        }
    }

    public boolean isShowRedHintImg() {
        return isShowRedHintImg;
    }

    public void setShowRedHintImg(boolean showRedHintImg) {
        isShowRedHintImg = showRedHintImg;
        redHintImg.setVisibility(showRedHintImg ? VISIBLE : GONE);
    }

    public String getJumpUrl() {
        return jumpUrl;
    }

    public void setJumpUrl(String jumpUrl) {
        if (jumpUrl != null) {
            this.jumpUrl = jumpUrl;
        }
    }

    public String getOnclickId() {
        return onclickId;
    }

    public void setOnclickId(String onclickId) {
        this.onclickId = onclickId;
    }

    public MenuItemLayout(Context context) {
        this(context, null);
    }

    public MenuItemLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MenuItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        mContext = context;
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mView = inflater.inflate(R.layout.item_menu_layout, this, true);
        titleTv = (TextView) mView.findViewById(R.id.menu_item_text);
        hintTv = (TextView) mView.findViewById(R.id.menu_item_text_hint);
        iconImg = (ImageView) mView.findViewById(R.id.menu_item_icon_img);
        redHintImg = (ImageView) mView.findViewById(R.id.menu_item_red_hint);

        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuItemLayout);
        setTitleText(a.getString(R.styleable.MenuItemLayout_title_text));
        setHintText(a.getString(R.styleable.MenuItemLayout_hint_text));
        setIconImgId(a.getResourceId(R.styleable.MenuItemLayout_icon_reference, 10000));
        setJumpUrl(a.getString(R.styleable.MenuItemLayout_jump_url));
        divideLineStyle = a.getInt(R.styleable.MenuItemLayout_divide_line_style, NO_LINE);
        setDivideLine(divideLineStyle);
    }

    public void setDivideLine(int bootomLineStyle) {
        View lineView = findViewById(R.id.divide_line_view);
        View areaView = findViewById(R.id.divide_area_view);
        lineView.setVisibility(GONE);
        areaView.setVisibility(GONE);
        if (bootomLineStyle == DIVIDE_LINE) {
            lineView.setVisibility(VISIBLE);
        } else if (bootomLineStyle == DIVIDE_AREA) {
            areaView.setVisibility(VISIBLE);
        }
    }

    public void setViewOnlickListener(OnClickListener onlickListener) {
        this.onClickListener = onlickListener;
        mView.setOnClickListener(onlickListener);
    }

    public TextView getTitleTv() {
        return titleTv;
    }

    public TextView getHintTv() {
        return hintTv;
    }
}

3.在布局中引用MenuItemLayout:
创建MainActivity为应用的入口,修改activity_main.xml代码:
在布局中我随便写了几个菜单作为参考,菜单项目可以再布局中写,也可以在代码中动态生成然后添加进布局中(可以支持后台动态配置)。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/main_bg"
    tools:context="com.example.tianxiying.mytestapplication.MainActivity">

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="0"
        app:icon_reference="@mipmap/ic_launcher"
        app:title_text="个人中心" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="1"
        app:icon_reference="@mipmap/ic_launcher"
        app:hint_text="查看收藏过的书签"
        app:title_text="我的收藏" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="1"
        app:icon_reference="@mipmap/ic_launcher"
        app:hint_text="我关注过的人在这里"
        app:title_text="我的关注" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="2"
        app:icon_reference="@mipmap/ic_launcher"
        app:hint_text="有什么问题点这里"
        app:title_text="我的客服" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="1"
        app:icon_reference="@mipmap/ic_launcher"
        app:title_text="设置" />
</LinearLayout>

运行起来如图所示:
运行图.png

结尾:
以上只是组合自定义控件的一个简单的例子,目的在于叙述自定义控件的方法和思路,如果想要更多的效果可以重写组件的onMeasure、onLayout、onDraw来实现。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant