1. 程式人生 > >Android之高仿今日頭條、網易新聞首頁動態改變tab

Android之高仿今日頭條、網易新聞首頁動態改變tab

前言:
專案需要一個類似今日頭條或者網易新聞首頁動態改變tab(頻道欄目)的功能,進過一番折騰,目前已實現該功能。

先看看效果圖:
這裡寫圖片描述

思路:
1,關於tab欄目橫著滑動功能控制元件的選擇,這裡我採用的HorizontalScrollView,每個tab採用動態建立的方式。至於為什麼沒有選擇流行的TabLayout,是因為專案後期需求需要每個tab有一個長按的響應事件,但是TabLayout的長按事件不知道怎麼回事,總是無法響應,(有空會去研究)。
2,對欄目進行編輯介面的功能介紹:
①欄目分為當前使用者欄目和當前使用者沒有選擇的欄目(更多欄目),採用兩個GridView使用,但是整體又是可以上下滑動的,所以兩個GridView的外層是一個ScrollView,需要解決嵌套出現的問題。②過拽排序(附有動畫效果),當用戶在使用者欄目長按時,會出現震動,其中的第一個是不允許排序的(不能拖動),更多欄目只有點選事件,當點選時會把當前的tab移動到使用者欄目。③編輯介面返回時,需要重新設定首頁的tab欄目資料。
3,對欄目進行本地資料儲存,記錄使用者的每次對tab進行的修改。

MainActivity.java程式碼:

public class MainActivity extends AppCompatActivity {

    private ColumnHorizontalScrollView mColumnHorizontalScrollView; // 自定義HorizontalScrollView
    private LinearLayout mRadioGroup_content; // 每個標題

    private LinearLayout ll_more_columns; // 右邊+號的父佈局
    private ImageView button_more_columns; // 標題右邊的+號
private RelativeLayout rl_column; // +號左邊的佈局:包括HorizontalScrollView和左右陰影部分 public ImageView shade_left; // 左陰影部分 public ImageView shade_right; // 右陰影部分 private int columnSelectIndex = 0; // 當前選中的欄目索引 private int mItemWidth = 0; // Item寬度:每個標題的寬度 private int mScreenWidth = 0; // 螢幕寬度 public
final static int CHANNELREQUEST = 1; // 請求碼 public final static int CHANNELRESULT = 10; // 返回碼 // tab集合:HorizontalScrollView的資料來源 private ArrayList<ChannelItem> userChannelList = new ArrayList<ChannelItem>(); private ViewPager mViewPager; private ArrayList<Fragment> fragments = new ArrayList<Fragment>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mScreenWidth = Utils.getWindowsWidth(this); mItemWidth = mScreenWidth / 7; // 一個Item寬度為螢幕的1/7 initView(); } private void initView() { setContentView(R.layout.activity_main); mColumnHorizontalScrollView = (ColumnHorizontalScrollView) findViewById(R.id.mColumnHorizontalScrollView); mRadioGroup_content = (LinearLayout) findViewById(R.id.mRadioGroup_content); ll_more_columns = (LinearLayout) findViewById(R.id.ll_more_columns); rl_column = (RelativeLayout) findViewById(R.id.rl_column); button_more_columns = (ImageView) findViewById(R.id.button_more_columns); shade_left = (ImageView) findViewById(R.id.shade_left); shade_right = (ImageView) findViewById(R.id.shade_right); mViewPager = (ViewPager) findViewById(R.id.mViewPager); // + 號監聽 button_more_columns.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent_channel = new Intent(getApplicationContext(), ChannelActivity.class); startActivityForResult(intent_channel, CHANNELREQUEST); } }); setChangelView(); } /** * 當欄目項發生變化時候呼叫 */ private void setChangelView() { initColumnData(); initTabColumn(); initFragment(); } /** * 獲取Column欄目 資料 */ private void initColumnData() { userChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppApplication.getApp() .getSQLHelper()).getUserChannel()); } /** * 初始化Column欄目項 */ private void initTabColumn() { mRadioGroup_content.removeAllViews(); int count = userChannelList.size(); mColumnHorizontalScrollView.setParam(this, mScreenWidth, mRadioGroup_content, shade_left, shade_right, ll_more_columns, rl_column); for (int i = 0; i < count; i++) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mItemWidth, ViewGroup.LayoutParams.WRAP_CONTENT); params.leftMargin = 5; params.rightMargin = 5; TextView columnTextView = new TextView(this); columnTextView.setGravity(Gravity.CENTER); columnTextView.setPadding(5, 5, 5, 5); columnTextView.setId(i); columnTextView.setText(userChannelList.get(i).getName()); columnTextView.setTextColor(getResources().getColorStateList(R.color.top_category_scroll_text_color_day)); if (columnSelectIndex == i) { columnTextView.setSelected(true); } // 單擊監聽 columnTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (int i = 0; i < mRadioGroup_content.getChildCount(); i++) { View localView = mRadioGroup_content.getChildAt(i); if (localView != v) { localView.setSelected(false); } else { localView.setSelected(true); mViewPager.setCurrentItem(i); } } Toast.makeText(getApplicationContext(), userChannelList.get(v.getId()).getName(), Toast.LENGTH_SHORT).show(); } }); mRadioGroup_content.addView(columnTextView, i, params); } } /** * 初始化Fragment */ private void initFragment() { fragments.clear();//清空 int count = userChannelList.size(); for (int i = 0; i < count; i++) { NewsFragment newfragment = new NewsFragment(); fragments.add(newfragment); } NewsFragmentPagerAdapter mAdapetr = new NewsFragmentPagerAdapter(getSupportFragmentManager(), fragments); mViewPager.setAdapter(mAdapetr); mViewPager.addOnPageChangeListener(pageListener); } /** * ViewPager切換監聽方法 */ public ViewPager.OnPageChangeListener pageListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrollStateChanged(int arg0) { } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageSelected(int position) { mViewPager.setCurrentItem(position); selectTab(position); } }; /** * 選擇的Column裡面的Tab */ private void selectTab(int tab_postion) { columnSelectIndex = tab_postion; for (int i = 0; i < mRadioGroup_content.getChildCount(); i++) { View checkView = mRadioGroup_content.getChildAt(tab_postion); int k = checkView.getMeasuredWidth(); int l = checkView.getLeft(); int i2 = l + k / 2 - mScreenWidth / 2; mColumnHorizontalScrollView.smoothScrollTo(i2, 0); } //判斷是否選中 for (int j = 0; j < mRadioGroup_content.getChildCount(); j++) { View checkView = mRadioGroup_content.getChildAt(j); boolean ischeck; if (j == tab_postion) { ischeck = true; } else { ischeck = false; } checkView.setSelected(ischeck); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case CHANNELREQUEST: if (resultCode == CHANNELRESULT) { setChangelView(); } break; default: break; } super.onActivityResult(requestCode, resultCode, data); } }

主頁是控制元件和資料初始化工作。可以橫向滑動的是自定義的HorizontalScrollView——ColumnHorizontalScrollView,程式碼如下:

public class ColumnHorizontalScrollView extends HorizontalScrollView {

    /**
     * 傳入整體佈局
     */
    private View ll_content;
    /**
     * 傳入更多欄目選擇佈局
     */
    private View ll_more;
    /**
     * 傳入拖動欄佈局
     */
    private View rl_column;
    /**
     * 左陰影圖片
     */
    private ImageView leftImage;
    /**
     * 右陰影圖片
     */
    private ImageView rightImage;
    /**
     * 螢幕寬度
     */
    private int mScreenWitdh = 0;
    /**
     * 父類的活動activity
     */
    private Activity activity;

    public ColumnHorizontalScrollView(Context context) {
        super(context);
    }

    public ColumnHorizontalScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ColumnHorizontalScrollView(Context context, AttributeSet attrs,
                                      int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 在拖動的時候執行
     */
    @Override
    protected void onScrollChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
        // TODO Auto-generated method stub
        super.onScrollChanged(paramInt1, paramInt2, paramInt3, paramInt4);
        shade_ShowOrHide();
        if (!activity.isFinishing() && ll_content != null && leftImage != null && rightImage != null && ll_more != null && rl_column != null) {
            if (ll_content.getWidth() <= mScreenWitdh) {
                leftImage.setVisibility(View.GONE);
                rightImage.setVisibility(View.GONE);
            }
        } else {
            return;
        }
        if (paramInt1 == 0) {
            leftImage.setVisibility(View.GONE);
            rightImage.setVisibility(View.VISIBLE);
            return;
        }
        if (ll_content.getWidth() - paramInt1 + ll_more.getWidth() + rl_column.getLeft() == mScreenWitdh) {
            leftImage.setVisibility(View.VISIBLE);
            rightImage.setVisibility(View.GONE);
            return;
        }
        leftImage.setVisibility(View.VISIBLE);
        rightImage.setVisibility(View.VISIBLE);
    }

    /**
     * 傳入父類佈局中的資原始檔
     */
    public void setParam(Activity activity, int mScreenWitdh, View paramView1, ImageView paramView2, ImageView paramView3, View paramView4, View paramView5) {
        this.activity = activity;
        this.mScreenWitdh = mScreenWitdh;
        ll_content = paramView1;
        leftImage = paramView2;
        rightImage = paramView3;
        ll_more = paramView4;
        rl_column = paramView5;
    }

    /**
     * 判斷左右陰影的顯示隱藏效果
     */
    public void shade_ShowOrHide() {
        if (!activity.isFinishing() && ll_content != null) {
            measure(0, 0);
            //如果整體寬度小於螢幕寬度的話,那左右陰影都隱藏
            if (mScreenWitdh >= getMeasuredWidth()) {
                leftImage.setVisibility(View.GONE);
                rightImage.setVisibility(View.GONE);
            }
        } else {
            return;
        }
        //如果滑動在最左邊時候,左邊陰影隱藏,右邊顯示
        if (getLeft() == 0) {
            leftImage.setVisibility(View.GONE);
            rightImage.setVisibility(View.VISIBLE);
            return;
        }
        //如果滑動在最右邊時候,左邊陰影顯示,右邊隱藏
        if (getRight() == getMeasuredWidth() - mScreenWitdh) {
            leftImage.setVisibility(View.VISIBLE);
            rightImage.setVisibility(View.GONE);
            return;
        }
        //否則,說明在中間位置,左、右陰影都顯示
        leftImage.setVisibility(View.VISIBLE);
        rightImage.setVisibility(View.VISIBLE);
    }
}

完成了滑動改變tab索引,左右陰影效果。

使用者欄目編輯介面程式碼

public class ChannelActivity extends GestureDetectorActivity implements AdapterView.OnItemClickListener {

    /**
     * 使用者欄目
     */
    private DragGrid userGridView; // GridView
    DragAdapter userAdapter; // 介面卡
    ArrayList<ChannelItem> userChannelList = new ArrayList<ChannelItem>();

    /**
     * 其它欄目
     */
    private OtherGridView otherGridView; // GridView
    OtherAdapter otherAdapter; // 介面卡
    ArrayList<ChannelItem> otherChannelList = new ArrayList<ChannelItem>(); // 資料來源

    /**
     * 是否在移動,由於是動畫結束後才進行的資料更替,設定這個限制為了避免操作太頻繁造成的資料錯亂。
     */
    boolean isMove = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.channel);
        initView();
        initData();
    }

    /**
     * 初始化資料
     */
    private void initData() {
        userChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppApplication.getApp().getSQLHelper()).getUserChannel());
        otherChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppApplication.getApp().getSQLHelper()).getOtherChannel());
        userAdapter = new DragAdapter(this, userChannelList);
        userGridView.setAdapter(userAdapter);
        otherAdapter = new OtherAdapter(this, otherChannelList);
        otherGridView.setAdapter(otherAdapter);
        //設定GRIDVIEW的ITEM的點選監聽
        otherGridView.setOnItemClickListener(this);
        userGridView.setOnItemClickListener(this);
    }

    /**
     * 初始化佈局
     */
    private void initView() {
        userGridView = (DragGrid) findViewById(R.id.userGridView);
        otherGridView = (OtherGridView) findViewById(R.id.otherGridView);
    }

    /**
     * GRIDVIEW對應的ITEM點選監聽介面
     */
    @Override
    public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
        //如果點選的時候,之前動畫還沒結束,那麼就讓點選事件無效
        if (isMove) {
            return;
        }
        switch (parent.getId()) {
            case R.id.userGridView:
                //position為 0 的不進行任何操作
                if (position != 0) {
                    final ImageView moveImageView = getView(view);
                    if (moveImageView != null) {
                        TextView newTextView = (TextView) view.findViewById(R.id.text_item);
                        final int[] startLocation = new int[2];
                        newTextView.getLocationInWindow(startLocation);
                        final ChannelItem channel = ((DragAdapter) parent.getAdapter()).getItem(position);
                        otherAdapter.setVisible(false);
                        //新增到最後一個
                        otherAdapter.addItem(channel);
                        new Handler().postDelayed(new Runnable() {
                            public void run() {
                                try {
                                    int[] endLocation = new int[2];
                                    //獲取終點的座標
                                    otherGridView.getChildAt(otherGridView.getLastVisiblePosition()).getLocationInWindow(endLocation);
                                    MoveAnim(moveImageView, startLocation, endLocation, channel, userGridView);
                                    userAdapter.setRemove(position);
                                } catch (Exception localException) {
                                }
                            }
                        }, 50L);
                    }
                }
                break;
            case R.id.otherGridView:
                // 其它GridView
                final ImageView moveImageView = getView(view);
                if (moveImageView != null) {
                    TextView newTextView = (TextView) view.findViewById(R.id.text_item);
                    final int[] startLocation = new int[2];
                    newTextView.getLocationInWindow(startLocation);
                    final ChannelItem channel = ((OtherAdapter) parent.getAdapter()).getItem(position);
                    userAdapter.setVisible(false);
                    //新增到最後一個
                    userAdapter.addItem(channel);
                    new Handler().postDelayed(new Runnable() {
                        public void run() {
                            try {
                                int[] endLocation = new int[2];
                                //獲取終點的座標
                                userGridView.getChildAt(userGridView.getLastVisiblePosition()).getLocationInWindow(endLocation);
                                MoveAnim(moveImageView, startLocation, endLocation, channel, otherGridView);
                                otherAdapter.setRemove(position);
                            } catch (Exception localException) {
                            }
                        }
                    }, 50L);
                }
                break;
            default:
                break;
        }
    }

    /**
     * 點選ITEM移動動畫
     *
     * @param moveView
     * @param startLocation
     * @param endLocation
     * @param moveChannel
     * @param clickGridView
     */
    private void MoveAnim(View moveView, int[] startLocation, int[] endLocation, final ChannelItem moveChannel,
                          final GridView clickGridView) {
        int[] initLocation = new int[2];
        //獲取傳遞過來的VIEW的座標
        moveView.getLocationInWindow(initLocation);
        //得到要移動的VIEW,並放入對應的容器中
        final ViewGroup moveViewGroup = getMoveViewGroup();
        final View mMoveView = getMoveView(moveViewGroup, moveView, initLocation);
        //建立移動動畫
        TranslateAnimation moveAnimation = new TranslateAnimation(
                startLocation[0], endLocation[0], startLocation[1],
                endLocation[1]);
        moveAnimation.setDuration(300L);
        //動畫配置
        AnimationSet moveAnimationSet = new AnimationSet(true);
        moveAnimationSet.setFillAfter(false);//動畫效果執行完畢後,View物件不保留在終止的位置
        moveAnimationSet.addAnimation(moveAnimation);
        mMoveView.startAnimation(moveAnimationSet);
        moveAnimationSet.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                isMove = true;
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                moveViewGroup.removeView(mMoveView);
                // instanceof 方法判斷2邊例項是不是一樣,判斷點選的是DragGrid還是OtherGridView
                if (clickGridView instanceof DragGrid) {
                    otherAdapter.setVisible(true);
                    otherAdapter.notifyDataSetChanged();
                    userAdapter.remove();
                } else {
                    userAdapter.setVisible(true);
                    userAdapter.notifyDataSetChanged();
                    otherAdapter.remove();
                }
                isMove = false;
            }
        });
    }

    /**
     * 獲取移動的VIEW,放入對應ViewGroup佈局容器
     *
     * @param viewGroup
     * @param view
     * @param initLocation
     * @return
     */
    private View getMoveView(ViewGroup viewGroup, View view, int[] initLocation) {
        int x = initLocation[0];
        int y = initLocation[1];
        viewGroup.addView(view);
        LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mLayoutParams.leftMargin = x;
        mLayoutParams.topMargin = y;
        view.setLayoutParams(mLayoutParams);
        return view;
    }

    /**
     * 建立移動的ITEM對應的ViewGroup佈局容器
     */
    private ViewGroup getMoveViewGroup() {
        ViewGroup moveViewGroup = (ViewGroup) getWindow().getDecorView();
        LinearLayout moveLinearLayout = new LinearLayout(this);
        moveLinearLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        moveViewGroup.addView(moveLinearLayout);
        return moveLinearLayout;
    }

    /**
     * 獲取點選的Item的對應View,
     *
     * @param view
     * @return
     */
    private ImageView getView(View view) {
        view.destroyDrawingCache();
        view.setDrawingCacheEnabled(true);
        Bitmap cache = Bitmap.createBitmap(view.getDrawingCache());
        view.setDrawingCacheEnabled(false);
        ImageView iv = new ImageView(this);
        iv.setImageBitmap(cache);
        return iv;
    }

    /**
     * 退出時候儲存選擇後資料庫的設定
     */
    private void saveChannel() {
        ChannelManage.getManage(AppApplication.getApp().getSQLHelper()).deleteAllChannel();
        ChannelManage.getManage(AppApplication.getApp().getSQLHelper()).saveUserChannel(userAdapter.getChannnelLst());
        ChannelManage.getManage(AppApplication.getApp().getSQLHelper()).saveOtherChannel(otherAdapter.getChannnelLst());
    }

    @Override
    public void onBackPressed() {
        saveChannel();
        if (userAdapter.isListChanged()) {
            Intent intent = new Intent(getApplicationContext(), MainActivity.class);
            setResult(MainActivity.CHANNELRESULT, intent);
            finish();
        } else {
            super.onBackPressed();
        }
    }
}

實現了拖動和新增動畫,排序功能。

剩下的就是介面卡和本地資料儲存程式碼,再不貼出來了。完整程式碼已上傳至github,關注公眾號“code小生”檢視原文,以及專案地址。