Reputation: 3423
I'm using a custom recyclerView that can have a footer and a header, that shoudnt influence the animations tho, here is a video of what's happening: http://www.videosprout.com/video?id=00fae6ac-39ff-47b6-b981-803d2773b67b
Why is every view moving one position back and then back to where it was instead of not doing that?
Here's my adapter:
public class AddEventsAdapter extends HFRecyclerViewAdapter<String, AddEventsAdapter.ViewHolder> {
public AddEventsAdapter(Context context) {
super(context);
}
@Override
public void footerOnVisibleItem() {
}
@Override
public void addData(int position, String item) {
super.addData(position, item);
}
@Override
public ViewHolder onCreateDataItemViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.event_item, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindDataItemViewHolder(ViewHolder holder, int position) {
holder.itemTv.setText(getData().get(position));
}
class ViewHolder extends RecyclerView.ViewHolder{
TextView itemTv;
public ViewHolder(View itemView) {
super(itemView);
itemTv = (TextView)itemView.findViewById(R.id.eventName);
}
}
}
The implementation:
final AddEventsAdapter MyAdapter = new AddEventsAdapter(this);
AddEventsRecycler.setAdapter(MyAdapter);
AddEventsRecycler.setLayoutManager(new LinearLayoutManager(this));
//add footer
final View footerView = LayoutInflater.from(this).inflate(R.layout.events_footer, AddEventsRecycler, false);
MyAdapter.setFooterView(footerView);
footerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyAdapter.addData(0, "Event number" + ++g);
}
});
ArrayList<String> data = new ArrayList<>();
data.add("Vacation");
MyAdapter.setData(data);
The custom RecyclerAdapter:
public abstract class HFRecyclerViewAdapter<T, VH extends RecyclerView.ViewHolder> extends BaseRecyclerViewAdapter<T> {
public HFRecyclerViewAdapter(Context context) {
super(context);
}
private static final int TYPE_HEADER = Integer.MAX_VALUE;
private static final int TYPE_FOOTER = Integer.MAX_VALUE - 1;
private static final int ITEM_MAX_TYPE = Integer.MAX_VALUE - 2;
private RecyclerView.ViewHolder headerViewHolder;
private RecyclerView.ViewHolder footerViewHolder;
class HFViewHolder extends RecyclerView.ViewHolder {
HFViewHolder(View v) {
super(v);
}
}
public void setHeaderView(View header){
if (headerViewHolder == null || header != headerViewHolder.itemView) {
headerViewHolder = new HFViewHolder(header);
notifyDataSetChanged();
}
}
public void setFooterView(View foot){
if (footerViewHolder == null || foot != footerViewHolder.itemView) {
footerViewHolder = new HFViewHolder(foot);
notifyDataSetChanged();
}
}
public void removeHeader(){
if (headerViewHolder != null){
headerViewHolder = null;
notifyDataSetChanged();
}
}
public void removeFooter(){
if (footerViewHolder != null){
footerViewHolder = null;
notifyDataSetChanged();
}
}
public boolean isHeader(int position){
return hasHeader() && position == 0;
}
public boolean isFooter(int position){
return hasFooter() && position == getDataItemCount() + (hasHeader() ? 1 : 0);
}
private int itemPositionInData(int rvPosition){
return rvPosition - (hasHeader() ? 1 : 0);
}
private int itemPositionInRV(int dataPosition){
return dataPosition + (hasHeader() ? 1 : 0);
}
@Override
public void notifyMyItemInserted(int itemPosition) {
notifyItemInserted(itemPositionInRV(itemPosition));
}
@Override
public void notifyMyItemRemoved(int itemPosition) {
notifyItemRemoved(itemPositionInRV(itemPosition));
}
@Override
public void notifyMyItemChanged(int itemPosition) {
notifyItemChanged(itemPositionInRV(itemPosition));
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
return headerViewHolder;
} else if (viewType == TYPE_FOOTER) {
return footerViewHolder;
}
return onCreateDataItemViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!isHeader(position) && !isFooter(position))
onBindDataItemViewHolder((VH)holder, itemPositionInData(position));
if (isFooter(position)){
footerOnVisibleItem();
}
}
public abstract void footerOnVisibleItem();
@Override
public int getItemCount() {
int itemCount = getDataItemCount();
if (hasHeader()) {
itemCount += 1;
}
if (hasFooter()) {
itemCount += 1;
}
return itemCount;
}
@Override
public int getItemViewType(int position) {
if (isHeader(position)) {
return TYPE_HEADER;
}
if (isFooter(position)) {
return TYPE_FOOTER;
}
int dataItemType = getDataItemType(itemPositionInData(position));
if (dataItemType > ITEM_MAX_TYPE) {
throw new IllegalStateException("getDataItemType() must be less than " + ITEM_MAX_TYPE + ".");
}
return dataItemType;
}
public int getDataItemCount() {
return super.getItemCount();
}
/**
* make sure your dataItemType < Integer.MAX_VALUE-1
*
* @param position item view position in rv
* @return item viewType
*/
public int getDataItemType(int position){
return 0;
}
public boolean hasHeader(){
return headerViewHolder != null;
}
public boolean hasFooter(){
return footerViewHolder != null;
}
public abstract VH onCreateDataItemViewHolder(ViewGroup parent, int viewType);
public abstract void onBindDataItemViewHolder(VH holder, int position);
}
EDIT: Same is happening when removing a view, with removeData(getAdapterPosition()
Upvotes: 2
Views: 141
Reputation: 17566
this is happening because the element is being added to at the beginning of the array (index 0). when this happens, the RecyclerView
will react as shown in the video, because the it pretends the backing data store is a list, and all elements are moved over one index, and the element is inserted in the beginning...which happens to be at the top.
you can see that it won't do this ugly behavior if elements are added at the end of the adapter: MyAdapter.addData(MyAdapter.getItemCount() - 1, "Event ");
. but of course, this is not what you want either, because it is the wrong index...and now it would seem like existing elements are actually jumping up one index on the GUI and things...but this is an interesting experiment to verify the theory.
still add elements at the end of the array using MyAdapter.addData(MyAdapter.getItemCount() - 1, "Event ");
, but make the LinearLayoutManager
display elements in reverse! this can be done by:
linearLayoutManager.setReverseLayout(true)
LinearLayoutManager(context,orientation,isReversed)
you may have to do something about the headers and footers are positioned in the adapter to make sure they stay as headers and footers in the RecyclerView.Adapter
though...once the RecyclerView.LayoutManager
is displaying things in reverse.
Upvotes: 1