概述
目的概述
要实现的控件效果如图(本图来自小米3)
如果说大家之前实现过的这种控件的,非常希望能和大家交流学习一下。
或者说如果大家知道类似这种效果的开源控件,也非常希望能够告知一下。谢谢!!
思路演化
- 先讲一下我一开始的思路
- 一开始我的想法是定义一个LinearLayout,不断在里面添加TextView的控件,在onlayout()方法中进行TextView的布局,并滑动到最尾端
- 滑动的话,然后通过Scroller实现滚动,当然滚动效果不是很好
- TextView点击事件,因为与滑动冲突,通过GestureDetector很好解决了这两者的关系
- 但是上面这种的效果不是很好,而且还涉及到了布局边缘位置的判断 ,处理得也不是很好。接下来讲一下我现在的实现方式,比较简单,主要采用了HorizontalScrollView布局实现
- 通过向HorizontalScrollView添加TextView,并滑动到最尾端
- 由于采用HorizontalScrollView,滑动与点击事件之间的冲突已经被解决,也不用进行边缘判断
进一步改造,实现第一个标签不动,只是其余标签滑动的效果。看了一下小米对于这个布局的实现即可明白,在最外层添加一个RelativeLayout,子布局为HorizontalScrollView,并添加第一个布局标签固定在左边遮挡住HorizontalScrollView的第一个标签即可
最后补充一点关于标签TextView右边箭头的实现,通过自定义一个继承TextView的控件,并在onDraw()方法中画出这样一条路径
代码实现
通过上面思路的讲解,下面展现实现的代码及效果图
- 标签TabView
public class TabView extends TextView {
/**
* 背景颜色
*/
private int mBgColor;
/**
* 线段颜色
*/
private int mLineColor;
/**
* 画笔
*/
private Paint mPaint;
/**
* 路径
*/
private Path mPath;
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
// 清除上次路径
mPaint.setColor(mBgColor);
canvas.drawPath(mPath, mPaint);
mPaint.setColor(mLineColor);
mPath.reset();
mPath.moveTo(getWidth() - 20 - getHeight() / 2, 0);
mPath.lineTo(getWidth() - 20, getHeight() / 2);
mPath.lineTo(getWidth() - 20 - getHeight() / 2, getHeight());
canvas.drawPath(mPath, mPaint);
}
public void setTab(String tab) {
setText(tab);
}
/**
* 初始化属性
*/
private void init() {
setGravity(Gravity.CENTER_VERTICAL);
setMaxLines(1);
mBgColor = getContext().getResources().getColor(
R.color.TabBackGroundColor);
mLineColor = getContext().getResources().getColor(R.color.TabLineColor);
setBackgroundColor(mBgColor);
mPath = new Path();
mPaint = new Paint();
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.STROKE);
}
public TabView(Context context) {
this(context, null);
}
public TabView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
}
- 布局FlowLayout
public class FlowLayout extends RelativeLayout implements View.OnClickListener {
/**
* Tab集合
*/
private List<String> mTabs;
/**
* 存放Tab的横向HorizontalScrollView
*/
private HorizontalScrollView mHsv;
/**
* 横向HorizontalScrollView的子布局
*/
private LinearLayout mHsvInnerLayout;
/**
* 添加一个新的标签
*
* @param text
*/
public void addTab(String text) {
mTabs.add(text);
if (getHsvInnerLayoutChildCount() == 0) {
// 为什么add的TextView的text没有居中,因为在addview后,高度为最高,再后来addview后,高度增加
// 添加了该tv,导致scrolleView最后面的View显示不全
addView(getTabView(text));
}
mHsvInnerLayout.addView(getTabView(text));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 滑到尾端
mHsv.smoothScrollTo(mHsvInnerLayout.getWidth(), 0);
}
/**
* 获取mHsvInnerLayout的子View数量
*
* @return
*/
private int getHsvInnerLayoutChildCount() {
return mHsvInnerLayout.getChildCount();
}
/**
* 根据tab生产View
*
* @param tab
* @return
*/
private View getTabView(String tab) {
View view = LayoutInflater.from(getContext()).inflate(
R.layout.item_file_tag, this, false);
TabView tv = (TabView) view.findViewById(R.id.item_tag);
tv.setTab(tab);
tv.setTag(tab); // 用于点击事件的判断
((TextView) tv).setTextColor(getTabColorStateList());
tv.setOnClickListener(this);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
view.setLayoutParams(params);
return view;
}
private void init() {
mTabs = new ArrayList<String>();
// 设置子view垂直方向上居中
setGravity(Gravity.CENTER_HORIZONTAL);
setBackgroundColor(getResources().getColor(R.color.TabBackGroundColor));
initHSV();
}
/**
* 初始化HorizontalScrollView
*/
private void initHSV() {
mHsv = new HorizontalScrollView(getContext());
// 隐藏滚动条
mHsv.setHorizontalScrollBarEnabled(false);
LayoutParams mHsvParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
mHsvParams.rightMargin = 50;
mHsvInnerLayout = new LinearLayout(getContext());
LayoutParams mHsvInnerLayoutParams = new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mHsvInnerLayout.setGravity(Gravity.CENTER_VERTICAL);
// 添加子View
mHsv.addView(mHsvInnerLayout, mHsvInnerLayoutParams);
addView(mHsv, mHsvParams);
}
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 获取ColorStateList对象
*
* @return
*/
private ColorStateList getTabColorStateList() {
ColorStateList csl = new ColorStateList(new int[][] {
{ android.R.attr.state_pressed },
{ -android.R.attr.state_pressed } }, new int[] { Color.BLACK,
Color.GRAY });
return csl;
}
private onTabClickListener mOnTabClickListener;
/**
* tab点击事件接口
*/
public interface onTabClickListener {
void onTabClick(String tab);
}
public void setOnTabClickListener(onTabClickListener l) {
this.mOnTabClickListener = l;
}
@Override
public void onClick(View v) {
String tab = (String) v.getTag();
if (tab == null)
return;
if (mOnTabClickListener != null) {
mOnTabClickListener.onTabClick(tab);
}
// 移除后面Tab
int index = mTabs.indexOf(tab);
int size = mTabs.size();
// 不作处理
if (index == -1 || index == size - 1) {
return;
}
for (int i = size - 1; i > index; i--) {
mHsvInnerLayout.removeViewAt(i);
mTabs.remove(i);
}
}
}
效果
- Demo下载地址
总结
- 总体来说没什么难度,但是还是值得学习记录一下过程思想
最后
最近可能开始要忙了,不过最近在学习Android开源框架Volley,所以接下来将会对记录一下我对Volley的理解。对Volley的用法,源码,设计模式,HTTP,自定义异常等方面做个记录
最后呢,如果你有其他思路或者开源控件,非常期待你的分享,谢谢。
- 如果本篇文章有哪里错误不足之处,或者哪方面可以做更好的封装,也希望能够指出,一起探讨学习。
最后
以上就是朴素小霸王为你收集整理的Android自定义控件之实现类似文件夹顶部的层层显示的横栏效果的全部内容,希望文章能够帮你解决Android自定义控件之实现类似文件夹顶部的层层显示的横栏效果所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复