Android之高仿今日头条、网易新闻首页动态改变tab
2016-12-07 本文已影响1213人
code小生
前言:
项目需要一个类似今日头条或者网易新闻首页动态改变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小生”查看原文,以及项目地址。