Android UI 模板设计之组合模式

引言:UI设计一直是程序设计中非常重要的一环,也是最让人抓狂的一环,好的设计需要不断修改、不断完善,那么我们如何才能减少UI设计中的重复劳动呢、如何在修改设计时减少工作量呢、如何一次开发,重复使用呢?

 文章目的:

  1. 掌握如何自定义控件属性
  2. 掌握如何动态创建组件
  3. 掌握如何设计动态模板
  4. 体会模板化开发的便利之处
  5. 体会接口回调机制的思想

1.如何自定义控件?

系统是怎么做的?以RelativeLayout为例:

第一步:在atts.xml中配置需要的属性

<declare-styleable name="RelativeLayout">
        <attr name="gravity" />
        <attr name="ignoreGravity" format="reference" />
</declare-styleable>

<declare-styleable name="RelativeLayout_Layout">
        <attr name="layout_toLeftOf" format="reference" />
        <attr name="layout_toRightOf" format="reference" />
        <attr name="layout_above" format="reference" />
        <attr name="layout_below" format="reference" />
        <attr name="layout_alignBaseline" format="reference" />
        <attr name="layout_alignLeft" format="reference" />
        <!-- 其他属性省略了。。。 -->
</declare-styleable>

第二步:重写控件,以满足自己的要求

第三步:在 xml 文件中使用控件

<?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=".MainActivity">

</RelativeLayout>

1.下面我们可以仿照系统的步骤来自定义我们的控件:

效果预览:

此控件由左右两个Button和中间一个TextView组合而成。

第一步:创建 atts.xml, 并配置需要的属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Topbar">
        <attr name="title" format="string"/>
        <attr name="titleTextSize" format="dimension"/>
        <attr name="titleTextColor" format="color"/>
        <attr name="leftText" format="string"/>
        <attr name="leftTextColor" format="color"/>
        <attr name="leftBackground" format="reference|color"/>
        <attr name="rightText" format="string"/>
        <attr name="rightTextColor" format="color"/>
        <attr name="rightBackground" format="reference|color"/>
    </declare-styleable>
</resources>

第二步:实现自己的 View 

package com.lzw.topbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.RequiresApi;

/**
 * 顶部导航栏控件
 * 这是一个组合控件,由左右两个Button和一个TextView组成
 */
public class TopBar extends RelativeLayout {

    // 步骤2:定义需要使用的控件
    private Button leftButton , rightButton;
    private TextView tvTitle;

    // 步骤3:声明这些控件所需要使用的属性,即之前在 atts.xml 中定义的属性

    // 左 Button 属性
    private String leftText;
    private int leftTextColor;
    private Drawable leftBackground;

    // 右 Button 属性
    private String rightText;
    private int rightTextColor;
    private Drawable rightBackground;

    // 中间 TextView 属性
    private String title;
    private int titleTextColor;
    private float titleTextSize;

    private LayoutParams leftParams,rightParams,titleParams;

    private topbarClickListener listener;

    public interface topbarClickListener{
        void leftClick();
        void rightClick();
    }

    public void setOnTopBarClickListener(topbarClickListener listener){
        this.listener = listener;
    }

    //  不需要自定义属性的构造方法
    public TopBar(Context context) {
        super(context);
    }

    /**
     * 步骤1:添加构造方法
     *        需要自定义属性就使用这一个构造方法,使用atts参数
     * @param context
     * @param attrs
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public TopBar(final Context context, AttributeSet attrs) {
        super(context, attrs);

        // 步骤4:给声明好的属性赋值,以将属性和控件关联
        // 4.1  通过TypedArray存储从xml文件中获取到的自定义属性的值,并赋给相应的变量
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);

        leftText = ta.getString(R.styleable.TopBar_leftText);
        leftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor,0);
        leftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);

        rightText = ta.getString(R.styleable.TopBar_rightText);
        rightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor,0);
        rightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);

        title = ta.getString(R.styleable.TopBar_title);
        titleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize,0);
        titleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor,0);

        // 回收。1.避免浪费资源 2.避免由于缓存而引起的错误
        ta.recycle();

        // 4.2  实例化控件
        leftButton = new Button(context);
        rightButton = new Button(context);
        tvTitle = new Button(context);

        // 4.3  将属性值和控件关联
        leftButton.setText(leftText);
        leftButton.setTextColor(leftTextColor);
        leftButton.setBackground(leftBackground);

        rightButton.setText(rightText);
        rightButton.setTextColor(rightTextColor);
        rightButton.setBackground(rightBackground);

        tvTitle.setText(title);
        tvTitle.setTextSize(titleTextSize);
        tvTitle.setTextColor(titleTextColor);
        tvTitle.setGravity(Gravity.CENTER);
        tvTitle.setBackgroundColor(Color.argb(0,0,0,0));

        setBackgroundColor(0xFFF59563);

        //  步骤5:将控件放到layout中,通过LayoutParams控制摆放方式
        leftParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);

        addView(leftButton, leftParams);

        rightParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);

        addView(rightButton, rightParams);

        titleParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.MATCH_PARENT);
        titleParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);

        addView(tvTitle, titleParams);

        //  步骤6:动态控制 TopBar
        leftButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.leftClick();
            }
        });

        rightButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.rightClick();
            }
        });

    }

    /**
     * 是否显示左Button
     * @param flag
     */
    public void setLeftIsVisable(boolean flag){
        if(flag){
            leftButton.setVisibility(View.VISIBLE);
        }else {
            leftButton.setVisibility(View.GONE);
        }
    }

}

第三步:使用

在Android Studio中引用一个第三方命名控件只要:添加xmlns:custom="http://schemas.android.com/apk/res-auto"即可

给重新引用的名字空间命名为custom,就可通过custom引用所有自定义的属性了

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.lzw.topbar.TopBar
        android:id="@+id/topbar"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        custom:leftBackground="#03A9F4"
        custom:leftText="Back"
        custom:leftTextColor="#E91E63"
        custom:rightBackground="#03A9F4"
        custom:rightText="More"
        custom:rightTextColor="#E91E63"
        custom:title="自定义标题"
        custom:titleTextColor="#123412"
        custom:titleTextSize="8sp"></com.lzw.topbar.TopBar>

</RelativeLayout>

2. 在MainActivity中使用TopBar

package com.lzw.topbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TopBar topBar = findViewById(R.id.topbar);
        topBar.setOnTopBarClickListener(new TopBar.topbarClickListener() {
            @Override
            public void leftClick() {
                Toast.makeText(MainActivity.this,"left",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void rightClick() {
                Toast.makeText(MainActivity.this,"right",Toast.LENGTH_SHORT).show();
            }
        });
        topBar.setLeftIsVisable(false);

    }
}

总结:

1.通过模板复用、接口回调可提高开发效率,降低代码耦合度

2.尽量完善模板,积累更多灵活、多功能的模板

3.模板不局限于UI设计,系统代码架构设计都可使用

4.遇到问题可以想想系统的实现方法

5.体会、思考经典的设计模式

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值