起因
因为大多数app都是自定义titleBar,但是每次都写xml的话很是繁琐,所以定义了一个我个人认为的适应各种场景的组件。
如上图所示,左边,中间,右边都是LinearLayout,里面可以动态添加view,通过addLeftAction()和addRightAction()方法添加。
案例
addLeftAction()和addRightAction()参数Action内置了三个Action,分别是ImageAction,TextAction,ViewAction,对应创建一个ImageView,TextView,View,这三个足以应对大部分场景了,其他特定的可以自己修改源码即可。
中间设置标题
TitleBar titleBar = findViewById(R.id.title_bar);
titleBar.setTitle("首页");
如果是沉浸式,titleBar延伸到了状态栏,可以使用以下方法来设置paddingTop达到不遮挡titleBar的效果
titleBar.setImmersive(true);
左侧添加返回按钮
titleBar.addLeftAction(new TitleBar.ImageAction(R.mipmap.ico_back) {
@Override
public void performAction(View view) {
finishAnimActivity();
}
});
右侧添加保存按钮
titleBar.addRightAction(new TitleBar.ImageAction(R.mipmap.ico_save) {
@Override
public void performAction(View view) {
}
});
源码
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import java.util.LinkedList;
/**
* @author kathline
*/
public class TitleBar extends ViewGroup implements View.OnClickListener {
private static final int DEFAULT_MAIN_TEXT_SIZE = 18;
private static final int DEFAULT_SUB_TEXT_SIZE = 14;
private static final int DEFAULT_ACTION_TEXT_SIZE = 15;
private static final int DEFAULT_TITLE_BAR_HEIGHT = 48;
private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
private LinearLayout mLeftLayout;
private LinearLayout mRightLayout;
private LinearLayout mCenterLayout;
private TextView mCenterText;
private TextView mSubTitleText;
private View mCustomCenterView;
private View mDividerView;
private boolean mImmersive;
private int mStatusBarHeight;
private int mActionPadding;
private int mOutPadding;
private int mActionTextColor;
private int mHeight;
public TitleBar(Context context) {
super(context);
init(context);
}
public TitleBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TitleBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
if (mImmersive) {
mStatusBarHeight = getStatusBarHeight();
}
mActionPadding = dip2px(5);
mOutPadding = dip2px(16);
mHeight = dip2px(DEFAULT_TITLE_BAR_HEIGHT);
initView(context);
}
private void initView(Context context) {
mLeftLayout = new LinearLayout(context);
mCenterLayout = new LinearLayout(context);
mRightLayout = new LinearLayout(context);
mDividerView = new View(context);
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mLeftLayout.setGravity(Gravity.CENTER);
mLeftLayout.setPadding(mOutPadding, mStatusBarHeight, mOutPadding, 0);
mCenterText = new TextView(context);
mSubTitleText = new TextView(context);
mCenterLayout.addView(mCenterText);
mCenterLayout.addView(mSubTitleText);
mCenterLayout.setPadding(mOutPadding, mStatusBarHeight, mOutPadding, 0);
setTitleTextStyle(Typeface.BOLD);
mCenterLayout.setGravity(Gravity.CENTER);
mCenterText.setTextSize(DEFAULT_MAIN_TEXT_SIZE);
mCenterText.setSingleLine();
mCenterText.setGravity(Gravity.CENTER);
mCenterText.setEllipsize(TextUtils.TruncateAt.END);
mSubTitleText.setTextSize(DEFAULT_SUB_TEXT_SIZE);
mSubTitleText.setSingleLine();
mSubTitleText.setGravity(Gravity.CENTER);
mSubTitleText.setEllipsize(TextUtils.TruncateAt.END);
mRightLayout.setPadding(mOutPadding, mStatusBarHeight, mOutPadding, 0);
addView(mLeftLayout, layoutParams);
addView(mCenterLayout);
addView(mRightLayout, layoutParams);
addView(mDividerView, new LayoutParams(LayoutParams.MATCH_PARENT, 1));
}
public void setImmersive(boolean immersive) {
mImmersive = immersive;
if (mImmersive) {
mStatusBarHeight = getStatusBarHeight();
} else {
mStatusBarHeight = 0;
}
}
public void setHeight(int height) {
mHeight = height;
setMeasuredDimension(getMeasuredWidth(), mHeight);
}
public void setActionPadding(int mActionPadding) {
this.mActionPadding = mActionPadding;
}
public void setOutPadding(int mOutPadding) {
this.mOutPadding = mOutPadding;
}
public void setLeftClickListener(OnClickListener l) {
mLeftLayout.setOnClickListener(l);
}
public void setTitle(CharSequence title) {
int index = title.toString().indexOf("\n");
if (index > 0) {
setTitle(title.subSequence(0, index), title.subSequence(index + 1, title.length()), LinearLayout.VERTICAL);
} else {
index = title.toString().indexOf("\t");
if (index > 0) {
setTitle(title.subSequence(0, index), " " + title.subSequence(index + 1, title.length()), LinearLayout.HORIZONTAL);
} else {
mCenterText.setText(title);
mSubTitleText.setVisibility(View.GONE);
}
}
}
private void setTitle(CharSequence title, CharSequence subTitle, int orientation) {
mCenterLayout.setOrientation(orientation);
mCenterText.setText(title);
mSubTitleText.setText(subTitle);
mSubTitleText.setVisibility(View.VISIBLE);
}
public void setCenterClickListener(OnClickListener l) {
mCenterLayout.setOnClickListener(l);
}
public void setTitle(int resid) {
setTitle(getResources().getString(resid));
}
public void setTitle(String title) {
mCenterText.setText(title);
mSubTitleText.setVisibility(View.GONE);
}
public void setTitleColor(@ColorInt int resid) {
mCenterText.setTextColor(resid);
}
public void setTitleSize(float size) {
mCenterText.setTextSize(size);
}
/**
* 加粗Typeface.BOLD
* @param textStyle
*/
public void setTitleTextStyle(int textStyle) {
mCenterText.setTypeface(Typeface.defaultFromStyle(textStyle));
}
public void setTitleBackground(int resid) {
mCenterText.setBackgroundResource(resid);
}
public void setSubTitleColor(int resid) {
mSubTitleText.setTextColor(resid);
}
public void setSubTitleSize(float size) {
mSubTitleText.setTextSize(size);
}
public void setCustomTitle(View titleView) {
if (titleView == null) {
mCenterText.setVisibility(View.VISIBLE);
if (mCustomCenterView != null) {
mCenterLayout.removeView(mCustomCenterView);
}
} else {
if (mCustomCenterView != null) {
mCenterLayout.removeView(mCustomCenterView);
}
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mCustomCenterView = titleView;
mCenterLayout.addView(titleView, layoutParams);
mCenterText.setVisibility(View.GONE);
}
}
public void setDivider(Drawable drawable) {
mDividerView.setBackgroundDrawable(drawable);
}
public void setDividerColor(int color) {
mDividerView.setBackgroundColor(color);
}
public void setDividerHeight(int dividerHeight) {
mDividerView.getLayoutParams().height = dividerHeight;
}
public void setActionTextColor(int colorResId) {
mActionTextColor = colorResId;
}
/**
* Function to set a click listener for Title TextView
*
* @param listener the onClickListener
*/
public void setOnTitleClickListener(OnClickListener listener) {
mCenterText.setOnClickListener(listener);
}
@Override
public void onClick(View view) {
final Object tag = view.getTag();
if (tag instanceof Action) {
final Action action = (Action) tag;
action.performAction(view);
}
}
/**
* Adds a list of {@link Action}s.
* @param actionList the actions to add
*/
public void addLeftActions(ActionList actionList) {
int actions = actionList.size();
for (int i = 0; i < actions; i++) {
addLeftAction(actionList.get(i));
}
}
/**
* Adds a new {@link Action}.
* @param action the action to add
*/
public View addLeftAction(Action action) {
final int index = mLeftLayout.getChildCount();
return addLeftAction(action, index);
}
/**
* Adds a new {@link Action} at the specified index.
* @param action the action to add
* @param index the position at which to add the action
*/
public View addLeftAction(Action action, int index) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
View view = inflateAction(action);
mLeftLayout.addView(view, index, params);
return view;
}
/**
* Removes all action views from this action bar
*/
public void removeLeftAllActions() {
mLeftLayout.removeAllViews();
}
/**
* Remove a action from the action bar.
* @param index position of action to remove
*/
public void removeLeftActionAt(int index) {
mLeftLayout.removeViewAt(index);
}
/**
* Remove a action from the action bar.
* @param action The action to remove
*/
public void removeLeftAction(Action action) {
int childCount = mLeftLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = mLeftLayout.getChildAt(i);
if (view != null) {
final Object tag = view.getTag();
if (tag instanceof Action && tag.equals(action)) {
mLeftLayout.removeView(view);
}
}
}
}
/**
* Returns the number of actions currently registered with the action bar.
* @return action count
*/
public int getLeftActionCount() {
return mLeftLayout.getChildCount();
}
/**
* Adds a list of {@link Action}s.
* @param actionList the actions to add
*/
public void addRightActions(ActionList actionList) {
int actions = actionList.size();
for (int i = 0; i < actions; i++) {
addRightAction(actionList.get(i));
}
}
/**
* Adds a new {@link Action}.
* @param action the action to add
*/
public View addRightAction(Action action) {
final int index = mRightLayout.getChildCount();
return addRightAction(action, index);
}
/**
* Adds a new {@link Action} at the specified index.
* @param action the action to add
* @param index the position at which to add the action
*/
public View addRightAction(Action action, int index) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
View view = inflateAction(action);
mRightLayout.addView(view, index, params);
return view;
}
/**
* Removes all action views from this action bar
*/
public void removeRightAllActions() {
mRightLayout.removeAllViews();
}
/**
* Remove a action from the action bar.
* @param index position of action to remove
*/
public void removeRightActionAt(int index) {
mRightLayout.removeViewAt(index);
}
/**
* Remove a action from the action bar.
* @param action The action to remove
*/
public void removeRightAction(Action action) {
int childCount = mRightLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = mRightLayout.getChildAt(i);
if (view != null) {
final Object tag = view.getTag();
if (tag instanceof Action && tag.equals(action)) {
mRightLayout.removeView(view);
}
}
}
}
/**
* Returns the number of actions currently registered with the action bar.
* @return action count
*/
public int getRightActionCount() {
return mRightLayout.getChildCount();
}
/**
* Inflates a {@link View} with the given {@link Action}.
* @param action the action to inflate
* @return a view
*/
private View inflateAction(Action action) {
View view = null;
if(action.getView() != null) {
view = action.getView();
}else if (TextUtils.isEmpty(action.getText())) {
ImageView img = new ImageView(getContext());
img.setImageResource(action.getDrawable());
view = img;
} else {
TextView text = new TextView(getContext());
text.setGravity(Gravity.CENTER);
text.setText(action.getText());
text.setTextSize(DEFAULT_ACTION_TEXT_SIZE);
if (mActionTextColor != 0) {
text.setTextColor(mActionTextColor);
}
view = text;
}
view.setPadding(mActionPadding, 0, mActionPadding, 0);
view.setTag(action);
view.setOnClickListener(this);
return view;
}
public TextView getTitleView(){
return mCenterText;
}
public View getViewByAction(Action action) {
View view = findViewWithTag(action);
return view;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height;
if (heightMode != MeasureSpec.EXACTLY) {
height = mHeight + mStatusBarHeight;
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
} else {
height = MeasureSpec.getSize(heightMeasureSpec) + mStatusBarHeight;
}
measureChild(mLeftLayout, widthMeasureSpec, heightMeasureSpec);
measureChild(mRightLayout, widthMeasureSpec, heightMeasureSpec);
if (mLeftLayout.getMeasuredWidth() > mRightLayout.getMeasuredWidth()) {
mCenterLayout.measure(
MeasureSpec.makeMeasureSpec(sizeWidth - 2 * mLeftLayout.getMeasuredWidth(), MeasureSpec.EXACTLY)
, heightMeasureSpec);
} else {
mCenterLayout.measure(
MeasureSpec.makeMeasureSpec(sizeWidth - 2 * mRightLayout.getMeasuredWidth(), MeasureSpec.EXACTLY)
, heightMeasureSpec);
}
measureChild(mDividerView, widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int sizeWidth = getMeasuredWidth();
mLeftLayout.layout(0, mStatusBarHeight, mLeftLayout.getMeasuredWidth(), mLeftLayout.getMeasuredHeight() + mStatusBarHeight);
mRightLayout.layout(sizeWidth - mRightLayout.getMeasuredWidth(), mStatusBarHeight,
sizeWidth, mRightLayout.getMeasuredHeight() + mStatusBarHeight);
if (mLeftLayout.getMeasuredWidth() > mRightLayout.getMeasuredWidth()) {
mCenterLayout.layout(mLeftLayout.getMeasuredWidth(), mStatusBarHeight,
sizeWidth - mLeftLayout.getMeasuredWidth(), getMeasuredHeight());
} else {
mCenterLayout.layout(mRightLayout.getMeasuredWidth(), mStatusBarHeight,
sizeWidth - mRightLayout.getMeasuredWidth(), getMeasuredHeight());
}
mDividerView.layout(0, getMeasuredHeight() - mDividerView.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight());
}
public static int dip2px(int dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int sp2px(int spValue) {
final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* 计算状态栏高度高度
* getStatusBarHeight
* @return
*/
public static int getStatusBarHeight() {
return getInternalDimensionSize(Resources.getSystem(), STATUS_BAR_HEIGHT_RES_NAME);
}
private static int getInternalDimensionSize(Resources res, String key) {
int result = 0;
int resourceId = res.getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId);
}
return result;
}
/**
* A {@link LinkedList} that holds a list of {@link Action}s.
*/
@SuppressWarnings("serial")
public static class ActionList extends LinkedList<Action> {
}
/**
* Definition of an action that could be performed, along with a ico_launcher to
* show.
*/
public interface Action {
View getView();
String getText();
int getDrawable();
void performAction(View view);
}
public static abstract class ViewAction implements Action {
private View mView;
public ViewAction(View view) {
mView = view;
}
@Override
public View getView() {
return mView;
}
@Override
public int getDrawable() {
return 0;
}
@Override
public String getText() {
return null;
}
}
public static abstract class ImageAction implements Action {
private int mDrawable;
public ImageAction(int drawable) {
mDrawable = drawable;
}
@Override
public View getView() {
return null;
}
@Override
public int getDrawable() {
return mDrawable;
}
@Override
public String getText() {
return null;
}
}
public static abstract class TextAction implements Action {
final private String mText;
public TextAction(String text) {
mText = text;
}
@Override
public View getView() {
return null;
}
@Override
public int getDrawable() {
return 0;
}
@Override
public String getText() {
return mText;
}
}
}