天天看点

Android自定义标题栏ActionBar

在日常的工作中,ActionBar使用的频率非常的高,而系统自带的ActionBar捆绑的东西有太多,而上面多数的效果我们都用不到。

如果不用系统的ActionBar,我们该怎样把自定义ActionBar的工作做到最简化呢。

首先我们做一个基本的需求分析。

Android自定义标题栏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();
                }
            });
        }
    }
           

有了上述代码,我们ActionBar的基本功能就可以使用了。但是,如果每个Activity里面都这么写,是不是会很繁琐。所以我们需要简单的封装一下。封装代码如下:

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:[email protected]
 */

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:[email protected]
 */

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

继续阅读