在日常的工作中,ActionBar使用的频率非常的高,而系统自带的ActionBar捆绑的东西有太多,而上面多数的效果我们都用不到。
如果不用系统的ActionBar,我们该怎样把自定义ActionBar的工作做到最简化呢。
首先我们做一个基本的需求分析。
如上图所示,我们的ActionBar通常包括了:左菜单, 标题, 右菜单 。如果要再细分一下, 菜单 = (图标 + 文本)。
需求确定后,代码就非常简单了。
actionbar_dft.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/lay_transroot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary"
android:orientation="vertical">
<View
android:id="@+id/v_statusbar"
android:layout_width="match_parent"
android:layout_height="1.0dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/actionbar_height"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/lay_actionbar_left"
android:layout_width="100dp"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_actionbar_left"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:background="@mipmap/ic_left_light"
android:visibility="gone" />
<TextView
android:id="@+id/tv_actionbar_left"
style="@style/text_white"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/iv_actionbar_left"
android:gravity="center_vertical"
android:maxLength="2"
android:singleLine="true"
android:text="@string/cancel"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:id="@+id/tv_actionbar_title"
style="@style/text_white"
android:layout_centerInParent="true"
android:maxLength="15"
android:text="@string/app_name"
android:textSize="16sp" />
<RelativeLayout
android:id="@+id/lay_actionbar_right"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="right"
android:orientation="horizontal">
<View
android:id="@+id/v_actionbar_right"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:visibility="gone" />
<TextView
android:id="@+id/tv_actionbar_right"
style="@style/text_white"
android:layout_height="match_parent"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@+id/v_actionbar_right"
android:gravity="center_vertical|right"
android:singleLine="true"
android:text="@string/confirm"
android:visibility="gone" />
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
到这里,有的人可能会说,你把标题栏做这么简单,稍微有复杂一点的需求,根本满足不了。对此,我只想表示,我做过的项目类似老外趣聊、英语趣配音、淘wifi,其中不乏千万用户的APP,这样的ActionBar基本上能满足产品需求,而且非常轻便。如果内聚做的好,更可以定制出你想要actionbar样式。
有了 布局文件,我们再结合需求,添加一个点击回调,一般项目中标题是没有点击事件的,所以只要有左菜单、右菜单的点击回调就行。
回调接口:
public interface ActionBarClickListener {
void onLeftClick();
void onRightClick();
}
接下来我们就讨论一下,代码中如何集成、封装。
做法一: 代码中使用 <include ... /> 函数。
示例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/actionbar"
layout="@layout/actionbar_dft"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_description"
style="@style/text_black"
android:padding="20dp" />
</LinearLayout>
做法非常的简单,曾经和一个面试官讨论过这种做法的弊端,面试官认为这种加载方式对性能有影响。这我是不认同的,include不但能提高代码的复用率,使代码更简洁,它还没有增加系统的GPU任务,所以,不会对性能造成影响。
使用的时候,inlude函数里一定要添加id,因为include的布局,layout中的id是不能传递的。
然后就是 Activity 里的实现了。
protected View layRoot;
protected View vStatusBar;
protected View layLeft;
protected View layRight;
protected TextView tvTitle;
protected TextView tvLeft;
protected TextView tvRight;
protected View iconLeft;
protected View iconRight;
/**
* 设置数据
*
* @param strTitle
* @param resIdLeft
* @param strLeft
* @param resIdRight
* @param strRight
* @param listener
*/
public void setMyActionBar(String strTitle, int resIdLeft, String strLeft, int resIdRight, String strRight, final ActionBarClickListener listener) {
View actionBar = findViewById(R.id.actionbar);
layRoot = actionBar.findViewById(R.id.lay_transroot);
vStatusBar = actionBar.findViewById(R.id.v_statusbar);
tvTitle = (TextView) actionBar.findViewById(R.id.tv_actionbar_title);
tvLeft = (TextView) actionBar.findViewById(R.id.tv_actionbar_left);
tvRight = (TextView) actionBar.findViewById(R.id.tv_actionbar_right);
iconLeft = actionBar.findViewById(R.id.iv_actionbar_left);
iconRight = actionBar.findViewById(R.id.v_actionbar_right);
if (!TextUtils.isEmpty(strTitle)) {
tvTitle.setText(strTitle);
} else {
tvTitle.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(strLeft)) {
tvLeft.setText(strLeft);
tvLeft.setVisibility(View.VISIBLE);
} else {
tvLeft.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(strRight)) {
tvRight.setText(strRight);
tvRight.setVisibility(View.VISIBLE);
} else {
tvRight.setVisibility(View.GONE);
}
if (resIdLeft == 0) {
iconLeft.setVisibility(View.GONE);
} else {
iconLeft.setBackgroundResource(resIdLeft);
iconLeft.setVisibility(View.VISIBLE);
}
if (resIdRight == 0) {
iconRight.setVisibility(View.GONE);
} else {
iconRight.setBackgroundResource(resIdRight);
iconRight.setVisibility(View.VISIBLE);
}
if (listener != null) {
layLeft = findViewById(R.id.lay_actionbar_left);
layRight = findViewById(R.id.lay_actionbar_right);
layLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onLeftClick();
}
});
layRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onRightClick();
}
});
}
}
package com.myactionbar.base;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import com.myactionbar.R;
import com.myactionbar.impl.ActionBarClickListener;
/**
* Created by 晖仔(Milo) on 2017-03-21.
* email:303767416@qq.com
*/
public abstract class FirstBaseActivity extends AppCompatActivity {
protected View layRoot;
protected View vStatusBar;
protected View layLeft;
protected View layRight;
protected TextView tvTitle;
protected TextView tvLeft;
protected TextView tvRight;
protected View iconLeft;
protected View iconRight;
protected abstract int getContentViewId();
protected abstract void init();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
init();
}
/**
* 设置数据
*
* @param strTitle
* @param resIdLeft
* @param strLeft
* @param resIdRight
* @param strRight
* @param listener
*/
public void setMyActionBar(String strTitle, int resIdLeft, String strLeft, int resIdRight, String strRight, final ActionBarClickListener listener) {
View actionBar = findViewById(R.id.actionbar);
layRoot = actionBar.findViewById(R.id.lay_transroot);
vStatusBar = actionBar.findViewById(R.id.v_statusbar);
tvTitle = (TextView) actionBar.findViewById(R.id.tv_actionbar_title);
tvLeft = (TextView) actionBar.findViewById(R.id.tv_actionbar_left);
tvRight = (TextView) actionBar.findViewById(R.id.tv_actionbar_right);
iconLeft = actionBar.findViewById(R.id.iv_actionbar_left);
iconRight = actionBar.findViewById(R.id.v_actionbar_right);
if (!TextUtils.isEmpty(strTitle)) {
tvTitle.setText(strTitle);
} else {
tvTitle.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(strLeft)) {
tvLeft.setText(strLeft);
tvLeft.setVisibility(View.VISIBLE);
} else {
tvLeft.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(strRight)) {
tvRight.setText(strRight);
tvRight.setVisibility(View.VISIBLE);
} else {
tvRight.setVisibility(View.GONE);
}
if (resIdLeft == 0) {
iconLeft.setVisibility(View.GONE);
} else {
iconLeft.setBackgroundResource(resIdLeft);
iconLeft.setVisibility(View.VISIBLE);
}
if (resIdRight == 0) {
iconRight.setVisibility(View.GONE);
} else {
iconRight.setBackgroundResource(resIdRight);
iconRight.setVisibility(View.VISIBLE);
}
if (listener != null) {
layLeft = findViewById(R.id.lay_actionbar_left);
layRight = findViewById(R.id.lay_actionbar_right);
layLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onLeftClick();
}
});
layRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onRightClick();
}
});
}
}
}
完成以上代码,我们只需要在具体的Activity中实现方法
public void setMyActionBar(String strTitle, int resIdLeft, String strLeft, int resIdRight, String strRight, final ActionBarClickListener listener)
就行了。
但是,不知道心细的同学发现没有。这样写代码有一个非常大的弊端,就是BaseActivity里多出了好多代码(我们知道BaseActivity肯定还有非常多的其他代码,所以,尽量让我们的BaseActivity越简单越好)。那有什么办法减少BaseActivity里的代码量呢,同时也提高我们自身代码的内聚(让actionBar只为actionBar而用)呢。
那这里我们就要用到做法二。
做法二:自定义名为MyActionBar 的 widget。
大致思想就是自定义View,选一个layout布局填充,layout里面包括ActionBar的所有内容(左菜单、标题、右菜单)。
代码如下:
package com.myactionbar.widget;
import android.content.Context;
import android.support.v4.graphics.ColorUtils;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.myactionbar.R;
import com.myactionbar.impl.ActionBarClickListener;
/**
* 支持渐变的 actionBar
* Created by 晖仔(Milo) on 2016/12/28.
* email:303767416@qq.com
*/
public final class MyActionBar extends LinearLayout {
private View layRoot;
private View vStatusBar;
private View layLeft;
private View layRight;
private TextView tvTitle;
private TextView tvLeft;
private TextView tvRight;
private View iconLeft;
private View iconRight;
public MyActionBar(Context context) {
this(context, null);
}
public MyActionBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyActionBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init() {
setOrientation(HORIZONTAL);
View contentView = inflate(getContext(), R.layout.actionbar_dft, this);
layRoot = contentView.findViewById(R.id.lay_transroot);
vStatusBar = contentView.findViewById(R.id.v_statusbar);
tvTitle = (TextView) contentView.findViewById(R.id.tv_actionbar_title);
tvLeft = (TextView) contentView.findViewById(R.id.tv_actionbar_left);
tvRight = (TextView) contentView.findViewById(R.id.tv_actionbar_right);
iconLeft = contentView.findViewById(R.id.iv_actionbar_left);
iconRight = contentView.findViewById(R.id.v_actionbar_right);
}
/**
* 设置状态栏高度
*
* @param statusBarHeight
*/
public void setStatusBarHeight(int statusBarHeight) {
ViewGroup.LayoutParams params = vStatusBar.getLayoutParams();
params.height = statusBarHeight;
vStatusBar.setLayoutParams(params);
}
/**
* 设置是否需要渐变
*
* @param translucent
*/
public void setNeedTranslucent(boolean translucent) {
if (translucent) {
layRoot.setBackgroundDrawable(null);
}
}
/**
* 设置标题
*
* @param strTitle
*/
public void setTitle(String strTitle) {
if (!TextUtils.isEmpty(strTitle)) {
tvTitle.setText(strTitle);
tvTitle.setVisibility(View.VISIBLE);
} else {
tvTitle.setVisibility(View.GONE);
}
}
/**
* 设置透明度
*
* @param transAlpha 0-255 之间
*/
public void setTranslucent(int transAlpha) {
layRoot.setBackgroundColor(ColorUtils.setAlphaComponent(getResources().getColor(R.color.primary), transAlpha));
tvTitle.setAlpha(transAlpha);
tvLeft.setAlpha(transAlpha);
tvRight.setAlpha(transAlpha);
iconLeft.setAlpha(transAlpha);
iconRight.setAlpha(transAlpha);
}
/**
* 设置数据
*
* @param strTitle
* @param resIdLeft
* @param strLeft
* @param resIdRight
* @param strRight
* @param listener
*/
public void setData(String strTitle, int resIdLeft, String strLeft, int resIdRight, String strRight, final ActionBarClickListener listener) {
if (!TextUtils.isEmpty(strTitle)) {
tvTitle.setText(strTitle);
} else {
tvTitle.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(strLeft)) {
tvLeft.setText(strLeft);
tvLeft.setVisibility(View.VISIBLE);
} else {
tvLeft.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(strRight)) {
tvRight.setText(strRight);
tvRight.setVisibility(View.VISIBLE);
} else {
tvRight.setVisibility(View.GONE);
}
if (resIdLeft == 0) {
iconLeft.setVisibility(View.GONE);
} else {
iconLeft.setBackgroundResource(resIdLeft);
iconLeft.setVisibility(View.VISIBLE);
}
if (resIdRight == 0) {
iconRight.setVisibility(View.GONE);
} else {
iconRight.setBackgroundResource(resIdRight);
iconRight.setVisibility(View.VISIBLE);
}
if (listener != null) {
layLeft = findViewById(R.id.lay_actionbar_left);
layRight = findViewById(R.id.lay_actionbar_right);
layLeft.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onLeftClick();
}
});
layRight.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onRightClick();
}
});
}
}
}
完成上述代码后,BaseActivity相应的封装就变得非常简单,只要简单的几行就能完成。
/**
* 设置ActionBar
*
* @param strTitle
* @param resIdLeft
* @param strLeft
* @param resIdRight
* @param strRight
* @param listener
*/
protected void setMyActionBar(String strTitle, int resIdLeft, String strLeft, int resIdRight, String strRight, final ActionBarClickListener listener) {
actionBar = (MyActionBar) findViewById(R.id.actionbar);
actionBar.setData(strTitle, resIdLeft, strLeft, resIdRight, strRight, listener);
}
/**
* 获取actionBar
*
* @return
*/
protected MyActionBar getMyActionBar() {
return actionBar;
}
是不是非常简单。
而它的用法和做法一种是一致的,只需要在具体Activity中实现:
protected void setMyActionBar(String strTitle, int resIdLeft, String strLeft, int resIdRight, String strRight, final ActionBarClickListener listener)
即可。
至此,自定义ActionBar大部分内容都已经讲完,下面提供一下DEMO下载地址,免积分的。
CSDN:http://download.csdn.net/detail/yanjunhui2011/9788974
GitHub: https://github.com/yanjunhui2014/MyActionbar