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; // 自定义HorizontalScrollViewprivate 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>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mScreenWidth = Utils.getWindowsWidth(this);mItemWidth = mScreenWidth / 7; // 一个Item宽度为屏幕的1/7initView();}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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onPageScrollStateChanged(int arg0) {}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic 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);}}@Overrideprotected 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);}/*** 在拖动的时候执行*/@Overrideprotected void onScrollChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {// TODO Auto-generated method stubsuper.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; // GridViewDragAdapter userAdapter; // 适配器ArrayList<ChannelItem> userChannelList = new ArrayList<ChannelItem>();/*** 其它栏目*/private OtherGridView otherGridView; // GridViewOtherAdapter otherAdapter; // 适配器ArrayList<ChannelItem> otherChannelList = new ArrayList<ChannelItem>(); // 数据源/*** 是否在移动,由于是动画结束后才进行的数据更替,设置这个限制为了避免操作太频繁造成的数据错乱。*/boolean isMove = false;@Overrideprotected 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点击监听接口*/@Overridepublic 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:// 其它GridViewfinal 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() {@Overridepublic void onAnimationStart(Animation animation) {isMove = true;}@Overridepublic void onAnimationRepeat(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {moveViewGroup.removeView(mMoveView);// instanceof 方法判断2边实例是不是一样,判断点击的是DragGrid还是OtherGridViewif (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());}@Overridepublic void onBackPressed() {saveChannel();if (userAdapter.isListChanged()) {Intent intent = new Intent(getApplicationContext(), MainActivity.class);setResult(MainActivity.CHANNELRESULT, intent);finish();} else {super.onBackPressed();}}
}
实现了拖动和添加动画,排序功能。
剩下的就是适配器和本地数据存储代码,再不贴出来了。完整代码已上传至github,关注公众号“code小生”查看原文,以及项目地址。
发布评论