最近项目中有一个需求,类似网易、腾讯新闻客户端的频道管理界面,可以对频道进行排序、增加、删除功能。网上也找了相关的资料,但是发现他们都有同一个问题,就是改变频道的顺序后,对应频道的内容并不会改变。这是FragmentPagerAdapter的notifyDataSetChanged()方法没有效果导致的,知道这个原因,下面提供解决方法。
这个主界面的源码:
public class MainActivity extends FragmentActivity implements View .OnClickListener {
private TabLayout mNewsTab;
private ViewPager mNewsVp;
/** 调整返回的RESULTCODE */
public final static int CHANNELRESULT = 10 ;
private ArrayList<ChannelItem> mUserChannelList;
private List<NewsFragment> mListFragment;
private NewsVpAdapter mNewsVpAdapter;
@Override
protected void onCreate (Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
findViewById(R.id.more_columns_tv).setOnClickListener(this );
initView();
}
private void initView () {
mNewsTab = (TabLayout) findViewById(R.id.news_tab);
mNewsVp = (ViewPager) findViewById(R.id.news_vp);
findViewById(R.id.more_columns_tv).setOnClickListener(this );
mListFragment = new ArrayList<>();
mNewsTab.setTabMode(TabLayout.MODE_SCROLLABLE);
mUserChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppContext.getApp().getSQLHelper()).getUserChannel());
intData();
mNewsVpAdapter = new NewsVpAdapter(getSupportFragmentManager(),mListFragment,mUserChannelList);
mNewsVp.setAdapter(mNewsVpAdapter);
mNewsTab.setupWithViewPager(mNewsVp);
}
private void intData () {
for (ChannelItem channelItem : mUserChannelList) {
NewsFragment newsFragment = new NewsFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("tag" , channelItem);
newsFragment.setArguments(bundle);
mListFragment.add(newsFragment);
}
initColumn();
}
private void initColumn () {
for (int i = 0 ; i < mUserChannelList.size(); i++) {
mNewsTab.addTab(mNewsTab.newTab().setText(mUserChannelList.get(i).getName()));
}
}
@Override
public void onClick (View v) {
Intent intent_channel = new Intent(this , ChannelActivity.class);
startActivityForResult(intent_channel, CHANNELRESULT);
}
@Override
public void onActivityResult (int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case CHANNELRESULT:
if (resultCode == CHANNELRESULT){
selectTab(0 );
setChangelView();
}
break ;
default :
break ;
}
super .onActivityResult(requestCode, resultCode, data);
}
private void selectTab (int position) {
mNewsTab.getTabAt(position).select();
}
private void setChangelView () {
mListFragment.clear();
mNewsTab.removeAllTabs();
mUserChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppContext.getApp().getSQLHelper()).getUserChannel());
List<Integer> integers = mNewsVpAdapter.getIntegers();
for (int i = 0 ; i < mUserChannelList.size(); i++) {
ChannelItem channelItem = mUserChannelList.get(i);
if (integers.contains(i)) {
channelItem.setNeedUpdate(true );
} else {
channelItem.setNeedUpdate(false );
}
NewsFragment newsFragment = new NewsFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("tag" , channelItem);
newsFragment.setArguments(bundle);
newsFragment.setIsUpdate(true );
mListFragment.add(newsFragment);
}
initColumn();
selectTab(0 );
mNewsVpAdapter.setData(mListFragment,mUserChannelList);
mNewsVp.setCurrentItem(0 , false );
mNewsTab.setScrollPosition(0 , 0 , true );
mNewsTab.scrollTo(0 ,0 );
Log.e("size" ,mNewsTab.getTabCount()+"tab" );
dynamicSetTabLayoutMode(mNewsTab);
}
/**
* 根据Tab合起来的长度动态修改tab的模式
*
* @param tabLayout TabLayout
*/
public static void dynamicSetTabLayoutMode (TabLayout tabLayout) {
int tabTotalWidth = 0 ;
for (int i = 0 ; i < tabLayout.getChildCount(); i++) {
final View view = tabLayout.getChildAt(i);
view.measure(0 , 0 );
tabTotalWidth += view.getMeasuredWidth();
}
if (tabTotalWidth <= getScreenSize(tabLayout.getContext()).x) {
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_FIXED);
} else {
tabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
}
}
/**
* 获取屏幕尺寸
*
* @param context 上下文
* @return 屏幕尺寸像素值,下标为0的值为宽,下标为1的值为高
*/
public static Point getScreenSize (Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point screenSize = new Point();
wm.getDefaultDisplay().getSize(screenSize);
return screenSize;
}
}
频道管理Activity的源码:
public class ChannelActivity extends Activity implements OnItemClickListener , View .OnClickListener {
public static String TAG = "ChannelActivity" ;
/**
* 用户栏目的GRIDVIEW
*/
private DragGrid userGridView;
/**
* 其它栏目的GRIDVIEW
*/
private OtherGridView otherGridView;
/**
* 用户栏目对应的适配器,可以拖动
*/
DragAdapter userAdapter;
/**
* 其它栏目对应的适配器
*/
OtherAdapter otherAdapter;
/**
* 其它栏目列表
*/
ArrayList<ChannelItem> otherChannelList = new ArrayList<ChannelItem>();
/**
* 用户栏目列表
*/
ArrayList<ChannelItem> userChannelList = new ArrayList<ChannelItem>();
/**
* 是否在移动,由于这边是动画结束后才进行的数据更替,设置这个限制为了避免操作太频繁造成的数据错乱。
*/
boolean isMove = false ;
/**
* 手势监听
*/
GestureDetector mGestureDetector;
/**
* 是否需要监听手势关闭功能
*/
private boolean mNeedBackGesture = false ;
@Override
protected void onCreate (Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_channel);
initGestureDetector();
initView();
initData();
}
private void initGestureDetector () {
if (mGestureDetector == null ) {
mGestureDetector = new GestureDetector(getApplicationContext(),
new BackGestureListener(this ));
}
}
@Override
public boolean dispatchTouchEvent (MotionEvent ev) {
if (mNeedBackGesture) {
return mGestureDetector.onTouchEvent(ev) || super .dispatchTouchEvent(ev);
}
return super .dispatchTouchEvent(ev);
}
/**
* 初始化数据
*/
private void initData () {
userChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppContext.getApp().getSQLHelper()).getUserChannel());
otherChannelList = ((ArrayList<ChannelItem>) ChannelManage.getManage(AppContext.getApp().getSQLHelper()).getOtherChannel());
userAdapter = new DragAdapter(this , userChannelList);
userGridView.setAdapter(userAdapter);
otherAdapter = new OtherAdapter(this , otherChannelList);
otherGridView.setAdapter(otherAdapter);
otherGridView.setOnItemClickListener(this );
userGridView.setOnItemClickListener(this );
}
/**
* 初始化布局
*/
private void initView () {
userGridView = (DragGrid) findViewById(R.id.userGridView);
otherGridView = (OtherGridView) findViewById(R.id.otherGridView);
findViewById(R.id.title_bar).findViewById(R.id.back).setOnClickListener(this );
}
/**
* 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:
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) {
}
}
}, 50 L);
}
}
break ;
case R.id.otherGridView:
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) {
}
}
}, 50 L);
}
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 ];
moveView.getLocationInWindow(initLocation);
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(300 L);
AnimationSet moveAnimationSet = new AnimationSet(true );
moveAnimationSet.setFillAfter(false );
moveAnimationSet.addAnimation(moveAnimation);
mMoveView.startAnimation(moveAnimationSet);
moveAnimationSet.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart (Animation animation) {
isMove = true ;
}
@Override
public void onAnimationRepeat (Animation animation) {
}
@Override
public void onAnimationEnd (Animation animation) {
moveViewGroup.removeView(mMoveView);
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(LayoutParams.WRAP_CONTENT, 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(LayoutParams.MATCH_PARENT, 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(AppContext.getApp().getSQLHelper()).deleteAllChannel();
ChannelManage.getManage(AppContext.getApp().getSQLHelper()).saveUserChannel(userAdapter.getChannnelLst());
ChannelManage.getManage(AppContext.getApp().getSQLHelper()).saveOtherChannel(otherAdapter.getChannnelLst());
}
@Override
public void onClick (View v) {
saveChannel();
if (userAdapter.isListChanged()) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
setResult(MainActivity.CHANNELRESULT, intent);
finish();
Log.d(TAG, "数据发生改变" );
} else {
}
finish();
}
/**
* 返回手势监听接口
*/
public class BackGestureListener implements GestureDetector .OnGestureListener {
ChannelActivity activity;
public BackGestureListener (ChannelActivity activity) {
this .activity = activity;
}
@Override
public boolean onDown (MotionEvent e) {
return false ;
}
@Override
public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false ;
}
@Override
public void onLongPress (MotionEvent e) {
}
@Override
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if ((e2.getX() - e1.getX()) > 100 && Math.abs(e1.getY() - e2.getY()) < 60 ) {
activity.onBackPressed();
return true ;
}
return false ;
}
@Override
public void onShowPress (MotionEvent e) {
}
@Override
public boolean onSingleTapUp (MotionEvent e) {
return false ;
}
}
}
当你更新里fragment List集合后调用fragmentpageadpater的notifyDataSetChanged方法时发现数据根本就没有刷新。通过对fragmentpageadapter的源码查看你会在instantiateItem方法里面发现这一段:
String name = makeFragmentName(container. getId(), position);
Fragment fragment = mFragmentManager. findFragmentByTag(name);
if (fragment != null ) {
if (DEBUG) Log . v(TAG , "Attaching item #" + position + ": f=" + fragment);
mCurTransaction. attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log . v(TAG , "Adding item #" + position + ": f=" + fragment);
mCurTransaction. add(container. getId(), fragment,
makeFragmentName(container. getId(), position));
}
原来他会先去FragmentManager里面去查找有没有相关的fragment如果有就直接使用如果没有才会触发fragmentpageadapter的getItem方法获取一个fragment。所以你更新的fragmentList集合是没有作用的,解决方法是在instantiateItem方法里作文章,具体请看源码:
public class NewsVpAdapter extends FragmentPagerAdapter {
private List<NewsFragment> list_fragment;
private List<ChannelItem> channelItems;
private FragmentManager fm;
public boolean fragmentsUpdateFlag;
/**保存已经被初始化的Fragment所在的position*/
private List<Integer> integers = new ArrayList<>();
public List<Integer> getIntegers () {
return integers;
}
public void setIntegers (List<Integer> integers) {
this .integers = integers;
}
public NewsVpAdapter (FragmentManager fm, List<NewsFragment> list_fragment, List<ChannelItem> channelItems) {
super (fm);
this .list_fragment = list_fragment;
this .channelItems = channelItems;
this .fm = fm;
}
public void setData (List<NewsFragment> list_fragment, List<ChannelItem> channelItems) {
this .list_fragment = list_fragment;
this .channelItems = channelItems;
fragmentsUpdateFlag = true ;
notifyDataSetChanged();
}
@Override
public int getCount () {
return list_fragment.size();
}
@Override
public int getItemPosition (Object object) {
return POSITION_NONE;
}
@Override
public CharSequence getPageTitle (int position) {
return channelItems.get(position).getName();
}
int count;
@Override
public Fragment getItem (int position) {
return list_fragment.get(position);
}
@Override
public Object instantiateItem (ViewGroup container, int position) {
if (!integers.contains(position)) {
integers.add(position);
}
NewsFragment itemFragment = (NewsFragment) super .instantiateItem(container, position);
ChannelItem channelItem = channelItems.get(position);
String tag = itemFragment.getTag();
if (channelItem.getNeedUpdate() && integers.contains(position)) {
FragmentTransaction ft = fm.beginTransaction();
ft.remove(itemFragment);
itemFragment = (NewsFragment) list_fragment.get(position);
ft.add(container.getId(), itemFragment, tag);
ft.attach(itemFragment);
ft.commitAllowingStateLoss();
channelItem.setNeedUpdate(false );
}
return itemFragment;
}
}
代码注释得很清楚了,主要思路就是用新的fragment替换FragmentManager里缓存的旧的fragment,重点解释❶的地方,
String name = makeFragmentName(container.getId (), position)
Fragment fragment = mFragmentManager.findFragmentByTag (name)
说明fragmentpageadapter内部是用tag识别fragment的,并且有它自己的一套算法用于生成tag,所以我们这里必须用它生成的tag来添加新的fragment,否则fragmentpageadapter就无法识别这个新的fragment。通过channelItem.getNeedUpdate()得到的boolean类型来标识哪个fragment需要更新。
[DEMO下载](http://download.csdn.net/detail/run_forrest_run/9722482)
[DEMO apk下载](http://download.csdn.net/detail/run_forrest_run/9722517)