Android自定义组合控件--底部多按钮切换

效果图:
这里写图片描述
现在市场上大多数软件都是类似于上面的结构,底部有几个按钮用于切换到不同的界面。基于OOP思想,我想把下面的一整块布局封装成一个类,也就是我们的自定义组合控件—底部多按钮切换布局,我把它叫做BottomLayout

看上面的布局,几个按钮横向排列,我们先看一下布局
这里写图片描述
最外面LinearLayout 方向 horizontal,然后5个weight相同的RelativeLayout,每个RelativeLayout里面有一个Button(用了显示选中状态)个ImageView(用来显示红点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal" >

<RelativeLayout
android:id="@+id/rl_home"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true" >

<ImageView
android:id="@+id/iv_new_home"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentRight="true"
android:layout_margin="3dp"
android:src="@drawable/read_circle" />

<Button
android:id="@+id/btn_home"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@drawable/tab_home_selector"
android:clickable="false" />
</RelativeLayout>

<RelativeLayout
android:id="@+id/rl_encounter"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true" >

<Button
android:id="@+id/btn_encounter"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@drawable/tab_enconter_selector"
android:clickable="false" />
</RelativeLayout>

<RelativeLayout
android:id="@+id/rl_community"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true" >

<ImageView
android:id="@+id/iv_new_community"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentRight="true"
android:layout_margin="3dp"
android:src="@drawable/read_circle" />

<Button
android:id="@+id/btn_community"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@drawable/tab_community_selector"
android:clickable="false" />
</RelativeLayout>

<RelativeLayout
android:id="@+id/rl_message"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true" >

<ImageView
android:id="@+id/iv_new_message"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentRight="true"
android:layout_margin="3dp"
android:src="@drawable/read_circle" />

<Button
android:id="@+id/btn_message"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@drawable/tab_message_selector"
android:clickable="false" />
</RelativeLayout>

<RelativeLayout
android:id="@+id/rl_user"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true" >

<Button
android:id="@+id/btn_user"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@drawable/tab_user_selector"
android:clickable="false" />
</RelativeLayout>

</LinearLayout>

下面就是代码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package comzyh.bottomlayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

/**
* 主界面底部的按钮切换布局
*
* @version 1.0
* @author zyh
*/
public class BottomLayout extends LinearLayout implements OnClickListener {

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

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

public BottomLayout(Context context) {
this(context, null, 0);
}

private Context context;
private View rl_home, rl_encounter, rl_community, rl_message, rl_user;
private View iv_new_message, iv_new_home;
private Button btn_home, btn_encounter, btn_community, btn_message, btn_user;
private OnItemClickListener listener;
private int currentPosition = 0;

/**
* 初始化
*
* @version 1.0
* @author zyh
* @param context
*/
private void init(Context context) {
this.context = context;
LayoutInflater.from(context).inflate(R.layout.layout_bottom, this);
// 5块等分的布局
rl_home = findViewById(R.id.rl_home);
rl_encounter = findViewById(R.id.rl_encounter);
rl_community = findViewById(R.id.rl_community);
rl_message = findViewById(R.id.rl_message);
rl_user = findViewById(R.id.rl_user);

// 5个按钮
btn_home = (Button) findViewById(R.id.btn_home);
btn_encounter = (Button) findViewById(R.id.btn_encounter);
btn_community = (Button) findViewById(R.id.btn_community);
btn_message = (Button) findViewById(R.id.btn_message);
btn_user = (Button) findViewById(R.id.btn_user);

// 小圆点
iv_new_home = findViewById(R.id.iv_new_home);
iv_new_message = findViewById(R.id.iv_new_message);

hideHomeCircle();
hideMessageCircle();
changeButtonStatus(0);// 默认是位置0
setListener();
}

/**
* 显示首页按钮旁边的小红圈
*
* @version 1.0
* @author zyh
*/
public void showHomeCircle() {
iv_new_home.setVisibility(View.VISIBLE);
}

/**
* 隐藏首页按钮旁边的小红圈
*
* @version 1.0
* @author zyh
*/
public void hideHomeCircle() {
iv_new_home.setVisibility(View.GONE);
}

/**
* 显示首页按钮旁边的小红圈
*
* @version 1.0
* @author zyh
*/
public void showMessageCircle() {
iv_new_message.setVisibility(View.VISIBLE);
}

/**
* 隐藏首页按钮旁边的小红圈
*
* @version 1.0
* @author zyh
*/
public void hideMessageCircle() {
iv_new_message.setVisibility(View.GONE);
}

/**
* 为按钮设计按下监听
*
* @version 1.0
* @author zyh
*/
private void setListener() {
rl_home.setOnClickListener(this);
rl_encounter.setOnClickListener(this);
rl_community.setOnClickListener(this);
rl_message.setOnClickListener(this);
rl_user.setOnClickListener(this);
}

/**
* 提供给外部设置点击Item的接口
*
* @version 1.0
* @author zyh
* @param listener
*/
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}

public interface OnItemClickListener {
public void onItemClick(int position);
}

@Override
public void onClick(View v) {
currentPosition = 0;
switch (v.getId()) {
case R.id.rl_home:
currentPosition = 0;
break;
case R.id.rl_encounter:
currentPosition = 1;
break;
case R.id.rl_community:
currentPosition = 2;
break;
case R.id.rl_message:
currentPosition = 3;
break;
case R.id.rl_user:
currentPosition = 4;
break;
}
if (listener == null) {
return;
}
listener.onItemClick(currentPosition);
changeButtonStatus(currentPosition);
}

/**
* 根据当前位置改变按钮选中状态
*
* @version 1.0
* @author zyh
* @param position
*/
private void changeButtonStatus(int position) {
btn_home.setSelected(position == 0);
btn_encounter.setSelected(position == 1);
btn_community.setSelected(position == 2);
btn_message.setSelected(position == 3);
btn_user.setSelected(position == 4);
}
}

  • 设置5个RelativeLayout为点击区域,防止有的地方点击没反应,设置到Button上(Button的background有可能变形)
  • 封装Item点击的监听器
  • 提供显示隐藏小圆点的方法

上面的布局可以进行一点优化
这里写图片描述
查看布局层次,发现中间多了一层LinearLayout,为什么呢?
因为我们的BottomLayout本身继承自LinearLayout,而且我们在代码中这样写的

1
LayoutInflater.from(context).inflate(R.layout.layout_bottom, this);

又添加了一层布局,怎么优化呢?

  • 使用merge节点来消除冗余节点
    所以布局文件我们可以写成下面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
     <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="https://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:orientation="horizontal" >

    <RelativeLayout
    android:id="@+id/rl_home"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:clickable="true" >

    <ImageView
    android:id="@+id/iv_new_home"
    android:layout_width="10dp"
    android:layout_height="10dp"
    android:layout_alignParentRight="true"
    android:layout_margin="3dp"
    android:src="@drawable/read_circle" />


    <Button
    android:id="@+id/btn_home"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_centerInParent="true"
    android:background="@drawable/tab_home_selector"
    android:clickable="false" />
    </RelativeLayout>

    <RelativeLayout
    android:id="@+id/rl_encounter"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:clickable="true" >

    <Button
    android:id="@+id/btn_encounter"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_centerInParent="true"
    android:background="@drawable/tab_enconter_selector"
    android:clickable="false" />
    </RelativeLayout>

    <RelativeLayout
    android:id="@+id/rl_community"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:clickable="true" >

    <Button
    android:id="@+id/btn_community"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_centerInParent="true"
    android:background="@drawable/tab_community_selector"
    android:clickable="false" />
    </RelativeLayout>

    <RelativeLayout
    android:id="@+id/rl_message"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:clickable="true" >

    <ImageView
    android:id="@+id/iv_new_message"
    android:layout_width="10dp"
    android:layout_height="10dp"
    android:layout_alignParentRight="true"
    android:layout_margin="3dp"
    android:src="@drawable/read_circle" />

    <Button
    android:id="@+id/btn_message"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_centerInParent="true"
    android:background="@drawable/tab_message_selector"
    android:clickable="false" />
    </RelativeLayout>

    <RelativeLayout
    android:id="@+id/rl_user"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:clickable="true" >

    <Button
    android:id="@+id/btn_user"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_centerInParent="true"
    android:background="@drawable/tab_user_selector"
    android:clickable="false" />
    </RelativeLayout>
    </merge>
    这里写图片描述
    关于merge可以看我的这篇Android 视图优化merge标签分析

下面在主界面中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />

<comzyh.bottomlayout.BottomLayout
android:id="@+id/layout_bottom"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_alignParentBottom="true"
android:background="@android:color/darker_gray" />

</RelativeLayout>

使用自定义组合控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MainActivity extends Activity {

private BottomLayout layout_bottom;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
layout_bottom = (BottomLayout) findViewById(R.id.layout_bottom);
layout_bottom.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this, "点击了" + position, 0).show();
}
});

new Handler().postDelayed(new Runnable() {
public void run() {
layout_bottom.showHomeCircle();
}
}, 5000);

new Handler().postDelayed(new Runnable() {
public void run() {
layout_bottom.showMessageCircle();
}
}, 8000);
}

}

源代码下载