承接上一篇文章:Android高仿网易新闻客户端之首页,今天来实现动态添加标签效果。
动态标签页是一个流式布局,实现了宽度自动换行高度自动分配的功能,代码如下:
FlowLayout.java
package com.jackie.neteasenews; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; /** * Created by Jackie on 2015/12/30. * 流式布局 */ public class FlowLayout extends ViewGroup { public FlowLayout(Context context) { //new FlowLayout的时候会调用 this(context, null); } public FlowLayout(Context context, AttributeSet attrs) { //在布局文件中定义一个自定义View的时候(没有用到自定义属性) this(context, attrs, 0); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { //在布局文件中定义一个自定义View的时候(用到自定义属性) super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /** * MeasureSpec.EXACTLY //match_parent或者精确值 MeasureSpec.AT_MOST //warp_content 这种情况的View的宽度和高度是需要我们自己计算的 MeasureSpec.UNSPECIFIED //少见,子控件想要多大就多大 */ int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); int width = 0; int height = 0; //记录每一行的高度和宽度 int lineWidth = getPaddingLeft(); int lineHeight = getPaddingBottom(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); //测量子View的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams(); //子View占据的宽度 int childWidth = child.getMeasuredWidth() + params.leftMargin + params.rightMargin; //子View占据的高度 int childHeight = child.getMeasuredHeight() + params.topMargin + params.bottomMargin; if (lineWidth + childWidth < sizeWidth - getPaddingLeft() - getPaddingRight()) { //未换行 //叠加行宽 lineWidth += childWidth; //得到当前行的最大高度(同一行中所有子View中的最大高度) lineHeight = Math.max(lineHeight, childHeight); } else { //换行 /** * 换行之后,改行的宽和高就能确定 */ //对比得到最大的宽度(所有行中的最大行宽) width = Math.max(width, lineWidth); //记录高度 height += lineHeight; //重置lineWidth和lineHeight lineWidth = childWidth; lineHeight = childHeight; } //最后一个控件 if (i == childCount - 1) { width = Math.max(width, lineWidth); height += lineHeight; } } setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()); } /** * 存储每一行所有子View的集合 */ private List<List<View>> mAllView = new ArrayList<>(); /** * 每一行的高度 */ private List<Integer> mLineHeight = new ArrayList<>(); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllView.clear(); mLineHeight.clear(); //当前ViewGroup的宽度 int width = getWidth(); int lineWidth = 0; int lineHeight = 0; //存储每一行的所有子View List<View> lineView = new ArrayList<>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth() + params.leftMargin + params.rightMargin; int childHeight = child.getMeasuredHeight() + params.topMargin + params.bottomMargin; if (lineWidth + childWidth > width - getPaddingLeft() - getPaddingRight()) { //记录LineHeight mLineHeight.add(lineHeight); //记录当前行的View mAllView.add(lineView); //重置行宽和行高 lineWidth = 0; lineHeight = childHeight; //重置子View集合 lineView = new ArrayList<>(); } lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); lineView.add(child); } //最后一行 mLineHeight.add(lineHeight); mAllView.add(lineView); //设置子View的位置 int left = getPaddingLeft(); int top = getPaddingTop(); //行数 int lineCount = mAllView.size(); for (int i = 0; i < lineCount; i++) { //当前行的所有子View的集合 lineView = mAllView.get(i); lineHeight = mLineHeight.get(i); for (int j = 0; j < lineView.size(); j++) { View child = lineView.get(j); if (child.getVisibility() == View.GONE) { continue; } MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth() + params.leftMargin + params.rightMargin; int childLeft = left + params.leftMargin; int childTop = top + params.topMargin; int childRight = childLeft + child.getMeasuredWidth(); int childBottom = childTop + child.getMeasuredHeight(); //为子View设置布局 child.layout(childLeft, childTop, childRight, childBottom); //同一行所有子View高度相同,左边距叠加 left += childWidth; } left = getPaddingLeft(); top += lineHeight; } } /** * 与当前ViewGroup对应的LayoutParams */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } }
MainActivity.java
package com.jackie.neteasenews; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; import android.widget.PopupWindow; import android.widget.TextView; import com.viewpagerindicator.TabPageIndicator; import java.util.ArrayList; import java.util.List; public class MainActivity extends FragmentActivity { private TabPageIndicator mTabPageIndicator; private ViewPager mViewPager; private ViewPagerIndicatorAdapter mAdapter; private ImageButton mArrowButton; private LayoutInflater mInflater; private View mIndicatorView; private IndicatorPopupWindow mIndicatorPopupWindow; private FlowLayout mCurrentFlowLayout; private FlowLayout mAllFlowLayout; private HeadlineFragment mHeadlineFragment; private EnjoyFragment mEnjoyFragment; private HotspotFragment mHotspotFragment; private SportFragment mSportFragment; private HouseFragment mHouseFragment; private NBAFragment mNBAFragment; private CBAFragment mCBAFragment; private MoreFragment mMoreFragment; private List<Fragment> mFragmentList; private List<TextView> mCurrentItemList; private List<TextView> mAllItemList; private static final String[] INDICATOR_CURRENT_ITEM = new String[] { "头条", "娱乐", "热点", "体育", "房产", "NBA", "CBA" }; private static final String[] INDICATOR_ALL_ITEM = { "杭州", "财经", "科技", "跟帖", "直播", "时尚", "轻松一刻", "汽车", "段子", "军事", "历史", "家居", "原创", "游戏", "健康", "政务", "漫画", "哒哒","彩票", "手机", "移动互联", "中国足球", "社会", "影视", "国际足球", "跑步", "数码", "云课堂", "旅游", "读书", "酒香", "教育", "亲子", "暴雪游戏", "情感", "艺术", "值得买", "图片", "博客", "论坛", "订阅" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); data2View(); initEvent(); } private void initView() { mInflater = LayoutInflater.from(this); mTabPageIndicator = (TabPageIndicator) findViewById(R.id.indicator); mViewPager = (ViewPager) findViewById(R.id.viewpager); mArrowButton = (ImageButton) findViewById(R.id.indicator_arrow) ; mIndicatorView = mInflater.inflate(R.layout.activity_indicator, null); mIndicatorPopupWindow = new IndicatorPopupWindow(this, mIndicatorView); mCurrentFlowLayout = (FlowLayout) mIndicatorView.findViewById(R.id.current_flow_layout); mAllFlowLayout = (FlowLayout) mIndicatorView.findViewById(R.id.all_flow_layout); } private void initData() { mHeadlineFragment = new HeadlineFragment(); mEnjoyFragment = new EnjoyFragment(); mHotspotFragment = new HotspotFragment(); mSportFragment = new SportFragment(); mHouseFragment = new HouseFragment(); mNBAFragment = new NBAFragment(); mCBAFragment = new CBAFragment(); mFragmentList = new ArrayList<>(); mFragmentList.add(mHeadlineFragment); mFragmentList.add(mEnjoyFragment); mFragmentList.add(mHotspotFragment); mFragmentList.add(mSportFragment); mFragmentList.add(mHouseFragment); mFragmentList.add(mNBAFragment); mFragmentList.add(mCBAFragment); mCurrentItemList = new ArrayList<>(); mAllItemList = new ArrayList<>(); //初始化当前条目 for (int i = 0; i < INDICATOR_CURRENT_ITEM.length; i++) { TextView textView = (TextView) mInflater.inflate(R.layout.indicator_item_textview, mCurrentFlowLayout, false); textView.setText(INDICATOR_CURRENT_ITEM[i]); mCurrentItemList.add(textView); mCurrentFlowLayout.addView(textView); } //初始化候选条目 for (int i = 0; i < INDICATOR_ALL_ITEM.length; i++) { TextView textView = (TextView) mInflater.inflate(R.layout.indicator_item_textview, mAllFlowLayout, false); textView.setText(INDICATOR_ALL_ITEM[i]); mAllItemList.add(textView); mAllFlowLayout.addView(textView); } } private void data2View() { mAdapter = new ViewPagerIndicatorAdapter(getSupportFragmentManager(), mFragmentList, mCurrentItemList); mViewPager.setAdapter(mAdapter); //实例化ViewPagerIndicatorAdapter然后设置ViewPager与之关联 mTabPageIndicator.setViewPager(mViewPager, 0); } private void initEvent() { mArrowButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mArrowButton.setBackgroundResource(R.drawable.arrow_up); //弹出 mIndicatorPopupWindow.setAnimationStyle(R.style.popup_window_anim); mIndicatorPopupWindow.showAsDropDown(mArrowButton, 0, mTabPageIndicator.getHeight() / 2 - mArrowButton.getHeight()); } }); mIndicatorPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { mArrowButton.setBackgroundResource(R.drawable.arrow_down); } }); //更新指示器,会调用getPageTitle方法 mTabPageIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { mTabPageIndicator.notifyDataSetChanged(); } @Override public void onPageScrollStateChanged(int state) { } }); for (int i = 0; i < mCurrentItemList.size(); i++) { final int position = i; final TextView textView = mCurrentItemList.get(i); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mIndicatorPopupWindow.dismiss(); //直接显示 mTabPageIndicator.setViewPager(mViewPager, mCurrentItemList.indexOf(textView)); } }); //长按删除事件 textView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { mFragmentList.remove(position); mCurrentItemList.remove(position); mCurrentFlowLayout.removeView(textView); mAdapter.notifyDataSetChanged(); //更新指示器 mTabPageIndicator.notifyDataSetChanged(); mAllItemList.add(textView); mAllFlowLayout.addView(textView); //先将textView的长按事件移除,然后再重新初始化所有的点击事件 textView.setLongClickable(false); initEvent(); return true; } }); } for (int i = 0; i < mAllItemList.size(); i++) { final TextView textView = mAllItemList.get(i); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //添加新的Fragment mMoreFragment = new MoreFragment(textView.getText().toString()); mFragmentList.add(mMoreFragment); //添加相应的标题 mCurrentItemList.add(textView); //通知适配器数据更新 mAdapter.notifyDataSetChanged(); mAllItemList.remove(textView); mAllFlowLayout.removeView(textView); mCurrentFlowLayout.addView(textView); //重新初始化点击事件 /** * 从mAllItemList中移除一个元素添加到mCurrentItemList中 * 先将textView在mAllItemList的点击事件清除掉,然后再重新初始化所有的点击事件 * 否则,移除的元素的点击事件还是在mAllItemList中响应 */ textView.setClickable(false); initEvent(); } }); } } }效果图如下:
附上源码:
https://github.com/shineflower/NeteaseNews.git