Customize QQ Home Interface Tab
I took time to learn the blog of big sperm and downloaded it by the way, but it's really nauseous that CSDN does not have the function of one-click reload, so I'm bored!!!Of course, you can also go to his Xiaoo to learn Xiaoo. Click to jump to the original blog post
QQ Android version effect pasted first
You can see this cute tab, in fact, it's easy to get it out with the xml layout, but the blogger takes you all encapsulated into a custom control! (Look at the place where I darken, haha, the original blogger was single, although you go to sneak it)
Blogger effect
This speed.Sorry, bloggers don't know why it's so fast.
As you can see, there's plenty of support for packages as well, calculating widths based on the custom property tabWidht
In fact, it is very simple to achieve, the following bloggers with white people to achieve it, Daniel please ignore
Analysis
Question:
To achieve this effect, if we inherit View, then the text inside, the inner border, and the rounded corners will all be drawn by ourselves
It also supports font size changes and text alignment, which is obviously too expensive
In our usual xml layout, it's easy to think of linear layouts if we encounter similar effects
Then put several text controls inside and use weights to divide the width equally, so the idea for today's implementation is this, but usually we encapsulate the code in xml handwriting!
Overall ideas
1. Custom controls inherit LinearLayout and set themselves horizontally
2. Read custom attributes into classes
3. Add TextView controls to LinearLayout based on all custom properties
4. Finally, it's shown by the system (it's not our job at this point.)
Excess
1. Rounded corners are achieved by using the background and GradientDrawable is fully qualified
2. Add a TextView click event, change the selected subscript, and call the third step above!
3. Provide an interface for users to listen for selected subscripts and text
To be finished
First of all, international conventions determine the inherited parent
The three-parameter constructor is just a few of the steps we mentioned above
Supported properties and their default values
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="XTabHost"> <!--Radius--> <attr name="radius" format="dimension" /> <!--Text size--> <attr name="text_size" format="dimension" /> <!--Text Selected Color--> <attr name="text_select_color" format="color" /> <!--Text unselected color--> <attr name="text_unselect_color" format="color" /> <!--tab selected color--> <attr name="tab_select_color" format="color" /> <!--tab unselected color--> <attr name="tab_unselect_color" format="color" /> <!--tab spacing--> <attr name="tab_space" format="dimension" /> <!--tab width, used when wrapping--> <attr name="tab_width" format="dimension"/> <!--tab high, used when wrapping--> <attr name="tab_height" format="dimension"/> <!--The overall background--> <attr name="bg" format="color" /> <!--Defau lt number of displays--> <attr name="default_index" format="integer" /> <!--Displayed text array--> <attr name="src" format="reference" /> </declare-styleable> </resources>
Corresponds to a class
/**
* Background of your own controls
*/
private int backBg = Color.WHITE;
/**
* No tab selected background
*/
private int unSelectTabBg = Color.BLUE;
/**
* Background of selected tab
*/
private int selectTabBg = Color.WHITE;
/**
* The width and height of a Tab, which is used when it is a package
* -1 Indicates no effect, calculated as packaged children
* 80 Is the unit of dp
*/
private int tabWidth = 80, tabHeight = -1;
/**
* Color of unselected text
*/
private int unSelectTextColor = Color.WHITE;
/**
* Color of selected text
*/
private int selectTextColor = Color.BLUE;
/**
* Default font size, sp
*/
private int textSize = 16;
/**
* Spacing, px
*/
private int space = 1;
/**
* Rounded radius, dp
*/
private int radius = 0;
/**
* Current Subscript
*/
private int curIndex = 1;
/**
* All text to display
*/
private String[] textArr = new String[]{};
You can see that the effect of these properties is basically used by bloggers in the rendering.
Read Custom Properties
/**
* Read Custom Properties
*
* @param context
* @param attrs
*/
private void readAttr(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.XTabHost);
//Get custom properties
curIndex = (int) a.getInt(R.styleable.XTabHost_default_index, 0);
radius = a.getDimensionPixelSize(R.styleable.XTabHost_radius, dpToPx(radius));
backBg = a.getColor(R.styleable.XTabHost_bg, Color.WHITE);
unSelectTabBg = a.getColor(R.styleable.XTabHost_tab_unselect_color,
Color.parseColor("#51B5EF"));
selectTabBg = a.getColor(R.styleable.XTabHost_tab_select_color, Color.WHITE);
Color.WHITE);
Color.parseColor("#51B5EF"));
textSize = a.getDimensionPixelSize(R.styleable.XTabHost_text_size, 16);
space = a.getDimensionPixelSize(R.styleable.XTabHost_tab_space, 1);
tabWidth = a.getDimensionPixelSize(R.styleable.XTabHost_tab_width,
dpToPx(tabWidth));
tabHeight = a.getDimensionPixelSize(R.styleable.XTabHost_tab_height, -1);
CharSequence[] arr = a.getTextArray(R.styleable.XTabHost_src);
if (arr != null) {
String[] tArr = new String[arr.length];
for (int i = 0; i < arr.length; i++) {
tArr[i] = String.valueOf(arr[i]);
}
textArr = tArr;
}
a.recycle();
}
Since each read property is commented on at the time it is defined above, it is not explained
Generate effects based on supported attributes
/**
* Make an effect based on all the parameters
*/
private void sove() {
GradientDrawable dd = new GradientDrawable();
//Set rounded corners
dd.setCornerRadii(new float[]{radius, radius, radius, radius, radius, radius, radius, radius});
//Set background color
dd.setColor(backBg);
//Compatible with low versions
if (Build.VERSION.SDK_INT >= 16) {
setBackground(dd);
} else {
setBackgroundDrawable(dd);
}
//Remove all children
removeAllViews();
if (curIndex >= textArr.length || curIndex < 0) {
curIndex = 0;
}
for (int i = 0; i < textArr.length; i++) {
//Create a text
TextView tv = new TextView(getContext());
//Create layout objects for text
LayoutParams params = new LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT
);
if (i > 0) {
params.leftMargin = space;
}
GradientDrawable d = getFitGradientDrawable(i);
//Set the selected color and background if checked
if (curIndex == i) {
tv.setTextColor(selectTextColor);
d.setColor(selectTabBg);
} else {
tv.setTextColor(unSelectTextColor);
d.setColor(unSelectTabBg);
}
//Set Text
tv.setText(textArr[i]);
//Set text to appear in the middle
tv.setGravity(Gravity.CENTER);
//Set Text Size
tv.setTextSize(textSize);
//Set the background of the text, compatible with lower versions
if (Build.VERSION.SDK_INT >= 16) {
tv.setBackground(d);
} else {
//noinspection deprecation
tv.setBackgroundDrawable(d);
}
//Set the weight of the text (tab)
params.weight = 1;
tv.setLayoutParams(params);
tv.setTag(i);
tv.setOnClickListener(this);
//Add Children
addView(tv);
}
}
/**
* Get the background of each tab, also known as TextView, with rounded corners at the far left
* The rightmost side has a rounded effect on the right
* All four corners have rounded corners on the left and right
*
* @param index tab subscripting of
* @return
*/
private GradientDrawable getFitGradientDrawable(int index) {
GradientDrawable d = null;
//Round corners based on Subscripts
if (index == 0 && index == textArr.length - 1) {//If there is only one moment
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{radius, radius, radius, radius, radius, radius, radius, radius});
} else if (index == 0) { //If it is the leftmost, the upper left corner and the lower left corner are rounded
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{radius, radius, 0, 0, 0, 0, radius, radius});
} else if (index == textArr.length - 1) {//If it is the rightmost corner, the upper right corner and the lower right corner are rounded
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{0, 0, radius, radius, radius, radius, 0, 0});
} else { //If in the middle, then there are no rounded corners
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{0, 0, 0, 0, 0, 0, 0, 0});
}
return d;
}
This code highlights
The first long method, sove, is the third step in our analysis implementation process above, achieving results based on all the properties
It's really simple, first remove all the children, then add N TextView s based on the number of arrays of words, so that each
A TextView is a parent container whose width is divided equally and whose height is filled equally as it was written in xml
During the addition process we need to determine if the current Tab has rounded corners, because we can see the effect?
There are rounded corners on the left and also on the right, so the method getFitGradientDrawable(int index);
This is to get the background of the specified subscript, which is actually to get the background that each TextView should use
Before the for loop starts, we can see that we also set our own background, which is in all four corners.
Then at the end of the code, add a click event for each TextView, then toggle the selected TextView and uncheck it
The TextView effect of
Remaining Code
@Override
public void onClick(View v) {
//Get Subscript
int index = (int) v.getTag();
//If the click is the same, do not process
if (index == curIndex) {
return;
}
//Get the current TextView
TextView tv = (TextView) getChildAt(curIndex);
//Set to unselected text and background
tv.setTextColor(unSelectTextColor);
GradientDrawable d = getFitGradientDrawable(curIndex);
d.setColor(unSelectTabBg);
if (Build.VERSION.SDK_INT >= 16) {
tv.setBackground(d);
} else {
//noinspection deprecation
tv.setBackgroundDrawable(d);
}
//Record selected subscript
curIndex = index;
//Get the currently selected TextView
tv = (TextView) getChildAt(curIndex);
//Set to Selected Text and Selected Background
tv.setTextColor(selectTextColor);
d = getFitGradientDrawable(curIndex);
d.setColor(selectTabBg);
if (Build.VERSION.SDK_INT >= 16) {
tv.setBackground(d);
} else {
//noinspection deprecation
tv.setBackgroundDrawable(d);
}
//Notify users if they listen
if (mOnSelectListener != null) {
mOnSelectListener.onSelect(index, textArr[index]);
}
}
/**
* dp Units converted to px's
*
* @param dps
* @return
*/
int dpToPx(int dps) {
return Math.round(getResources().getDisplayMetrics().density * dps);
}
/**
* sp To px
*
* @param spVal
* @return
*/
int spToPx(float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}
private OnSelectListener mOnSelectListener;
/**
* Set up monitoring
*
* @param mOnSelectListener
*/
public void setOnSelectListener(OnSelectListener mOnSelectListener) {
this.mOnSelectListener = mOnSelectListener;
}
/**
* Callback interface
*/
public interface OnSelectListener {
/**
* callback
*
* @param index
* @param text
*/
void onSelect(int index, String text);
}
Paste out all the code below
/**
* Created by cxj on 2017/2/19.
* Tabs that mimic the qq main interface
*/
public class XTabHost extends LinearLayout implements View.OnClickListener {
public XTabHost(Context context) {
this(context, null);
}
public XTabHost(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public XTabHost(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//Set child alignment to horizontal
setOrientation(HORIZONTAL);
//Read Custom Properties
readAttr(context, attrs);
//Display effect
sove();
}
/**
* Read Custom Properties
*
* @param context
* @param attrs
*/
private void readAttr(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.XTabHost);
//Get custom properties
curIndex = (int) a.getInt(R.styleable.XTabHost_default_index, 0);
radius = a.getDimensionPixelSize(R.styleable.XTabHost_radius, dpToPx(radius));
backBg = a.getColor(R.styleable.XTabHost_bg, Color.WHITE);
unSelectTabBg = a.getColor(R.styleable.XTabHost_tab_unselect_color, Color.parseColor("#51B5EF"));
selectTabBg = a.getColor(R.styleable.XTabHost_tab_select_color, Color.WHITE);
unSelectTextColor = a.getColor(R.styleable.XTabHost_text_unselect_color, Color.WHITE);
selectTextColor = a.getColor(R.styleable.XTabHost_text_select_color, Color.parseColor("#51B5EF"));
textSize = a.getDimensionPixelSize(R.styleable.XTabHost_text_size, 16);
space = a.getDimensionPixelSize(R.styleable.XTabHost_tab_space, 1);
tabWidth = a.getDimensionPixelSize(R.styleable.XTabHost_tab_width, dpToPx(tabWidth));
tabHeight = a.getDimensionPixelSize(R.styleable.XTabHost_tab_height, -1);
CharSequence[] arr = a.getTextArray(R.styleable.XTabHost_src);
if (arr != null) {
String[] tArr = new String[arr.length];
for (int i = 0; i < arr.length; i++) {
tArr[i] = String.valueOf(arr[i]);
}
textArr = tArr;
}
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Get Calculation Modes
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
//Get recommended width and height
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
if (modeWidth == MeasureSpec.EXACTLY) { //If yes
} else { //If packaged or in a horizontal list
if (tabWidth > -1) {
for (int i = 0; i < getChildCount(); i++) {
TextView view = (TextView) getChildAt(i);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.width = tabWidth;
}
}
}
if (modeHeight == MeasureSpec.EXACTLY) { //If yes
} else { //If packaged or in a vertical list
if (tabHeight > -1) {
for (int i = 0; i < getChildCount(); i++) {
TextView view = (TextView) getChildAt(i);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.height = tabHeight;
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* Background of your own controls
*/
private int backBg = Color.WHITE;
/**
* No tab selected background
*/
private int unSelectTabBg = Color.BLUE;
/**
* Background of selected tab
*/
private int selectTabBg = Color.WHITE;
/**
* The width and height of a Tab, which is used when it is a package
* -1 Indicates no effect, calculated as packaged children
* 80 Is the unit of dp
*/
private int tabWidth = 80, tabHeight = -1;
/**
* Color of unselected text
*/
private int unSelectTextColor = Color.WHITE;
/**
* Color of selected text
*/
private int selectTextColor = Color.BLUE;
/**
* Default font size, sp
*/
private int textSize = 16;
/**
* Spacing, px
*/
private int space = 1;
/**
* Rounded radius, dp
*/
private int radius = 0;
/**
* Current Subscript
*/
private int curIndex = 1;
/**
* All text to display
*/
private String[] textArr = new String[]{};
/**
* Make an effect based on all the parameters
*/
private void sove() {
GradientDrawable dd = new GradientDrawable();
//Set rounded corners
dd.setCornerRadii(new float[]{radius, radius, radius, radius, radius, radius, radius, radius});
//Set background color
dd.setColor(backBg);
//Compatible with low versions
if (Build.VERSION.SDK_INT >= 16) {
setBackground(dd);
} else {
setBackgroundDrawable(dd);
}
//Remove all children
removeAllViews();
if (curIndex >= textArr.length || curIndex < 0) {
curIndex = 0;
}
for (int i = 0; i < textArr.length; i++) {
//Create a text
TextView tv = new TextView(getContext());
//Create layout objects for text
LayoutParams params = new LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT
);
if (i > 0) {
params.leftMargin = space;
}
GradientDrawable d = getFitGradientDrawable(i);
//Set the selected color and background if checked
if (curIndex == i) {
tv.setTextColor(selectTextColor);
d.setColor(selectTabBg);
} else {
tv.setTextColor(unSelectTextColor);
d.setColor(unSelectTabBg);
}
//Set Text
tv.setText(textArr[i]);
//Set text to appear in the middle
tv.setGravity(Gravity.CENTER);
//Set Text Size
tv.setTextSize(textSize);
//Set the background of the text, compatible with lower versions
if (Build.VERSION.SDK_INT >= 16) {
tv.setBackground(d);
} else {
//noinspection deprecation
tv.setBackgroundDrawable(d);
}
//Set the weight of the text (tab)
params.weight = 1;
tv.setLayoutParams(params);
tv.setTag(i);
tv.setOnClickListener(this);
//Add Children
addView(tv);
}
}
/**
* Get a background image of each tab, with rounded corners at the left
* The rightmost side has a rounded effect on the right
* All four corners have rounded corners on the left and right
*
* @param index tab subscripting of
* @return
*/
private GradientDrawable getFitGradientDrawable(int index) {
GradientDrawable d = null;
//Round corners based on Subscripts
if (index == 0 && index == textArr.length - 1) {
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{radius, radius, radius, radius, radius, radius, radius, radius});
} else if (index == 0) {
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{radius, radius, 0, 0, 0, 0, radius, radius});
} else if (index == textArr.length - 1) {
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{0, 0, radius, radius, radius, radius, 0, 0});
} else {
d = new GradientDrawable();
//Set rounded corners
d.setCornerRadii(new float[]{0, 0, 0, 0, 0, 0, 0, 0});
}
return d;
}
@Override
public void onClick(View v) {
//Get Subscript
int index = (int) v.getTag();
//If the click is the same, do not process
if (index == curIndex) {
return;
}
//Get the current TextView
TextView tv = (TextView) getChildAt(curIndex);
//Set to unselected text and background
tv.setTextColor(unSelectTextColor);
GradientDrawable d = getFitGradientDrawable(curIndex);
d.setColor(unSelectTabBg);
if (Build.VERSION.SDK_INT >= 16) {
tv.setBackground(d);
} else {
//noinspection deprecation
tv.setBackgroundDrawable(d);
}
//Record selected subscript
curIndex = index;
//Get the currently selected TextView
tv = (TextView) getChildAt(curIndex);
//Set to Selected Text and Selected Background
tv.setTextColor(selectTextColor);
d = getFitGradientDrawable(curIndex);
d.setColor(selectTabBg);
if (Build.VERSION.SDK_INT >= 16) {
tv.setBackground(d);
} else {
//noinspection deprecation
tv.setBackgroundDrawable(d);
}
//Notify users if they listen
if (mOnSelectListener != null) {
mOnSelectListener.onSelect(index, textArr[index]);
}
}
/**
* dp Units converted to px's
*
* @param dps
* @return
*/
int dpToPx(int dps) {
return Math.round(getResources().getDisplayMetrics().density * dps);
}
/**
* sp To px
*
* @param spVal
* @return
*/
int spToPx(float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}
private OnSelectListener mOnSelectListener;
/**
* Set up monitoring
*
* @param mOnSelectListener
*/
public void setOnSelectListener(OnSelectListener mOnSelectListener) {
this.mOnSelectListener = mOnSelectListener;
}
/**
* Callback interface
*/
public interface OnSelectListener {
/**
* callback
*
* @param index
* @param text
*/
void onSelect(int index, String text);
}
}
International practice Demo: Source Download
Learn, by the way, in reality, although xml can also be achieved, but this feeling of jiao tall aha-ha of course is mainly learning, and finally reload the record!!!En is excellent, and don't forget to introduce Feiha to me, even to the original Blogger