Android高仿网易新闻客户端之动态添加标签

承接上一篇文章: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

你可能感兴趣的:(Android网易动态添加标签)