Saturday, March 19, 2016

Recycle View Items animation crashing while adding first item

Leave a Comment

I am developing a small app where users can post something and comment on a post. I am using the Instamaterial project on github as a reference.

https://github.com/frogermcs/InstaMaterial/blob/master/app/src/main/java/io/github/froger/instamaterial/ui/activity/CommentsActivity.java

There is a comment activity with animation while users post a new comment. Everything is working cool ... but when I post the very first comment... the app is crashing and not showing the comment animation (scroll to top) I spent the whole day trying ... but could not find the solution.

public class CommentsActivity extends AppCompatActivity implements SendCommentButton.OnSendClickListener {     public static final String ARG_DRAWING_START_LOCATION = "arg_drawing_start_location";      LinearLayout contentRoot;     RecyclerView rvComments;     LinearLayout llAddComment;     EditText etComment;     SendCommentButton btnSendComment;      private CommentsAdapter commentsAdapter;     private int drawingStartLocation;     Toolbar toolbar;     private int currentPostID;      DatabaseHelper db;     private PreferencesManager preferencesManager;     ArrayList<PostResponse.PostComments> comments;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_comments);          toolbar = (Toolbar) findViewById(R.id.toolbar);         contentRoot = (LinearLayout) findViewById(R.id.contentRoot);         rvComments = (RecyclerView) findViewById(R.id.rvComments);         llAddComment = (LinearLayout) findViewById(R.id.llAddComment);         etComment = (EditText) findViewById(R.id.etComment);         btnSendComment = (SendCommentButton) findViewById(R.id.btnSendComment);          preferencesManager = new PreferencesManager(CommentsActivity.this);         db = new DatabaseHelper(CommentsActivity.this);          setupToolbar();         setupComments();         setupSendCommentButton();          drawingStartLocation = getIntent().getIntExtra(ARG_DRAWING_START_LOCATION, 0);         if (savedInstanceState == null) {             contentRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {                 @Override                 public boolean onPreDraw() {                     contentRoot.getViewTreeObserver().removeOnPreDrawListener(this);                     startIntroAnimation();                     return true;                 }             });         }     }      protected void setupToolbar() {         if (toolbar != null) {             setSupportActionBar(toolbar);             getSupportActionBar().setDisplayShow HomeEnabled(true);             toolbar.setNavigationIcon(R.mipmap.ic_menu_cancel);             toolbar.setNavigationOnClickListener(new View.OnClickListener() {                 @Override                 public void onClick(View v) {                     onBackPressed();                 }             });         }     }      private void setupComments() {         LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);         rvComments.setLayoutManager(linearLayoutManager);         rvComments.setHasFixedSize(true);         currentPostID = getIntent().getIntExtra(PostResponse.KEY_ID, -1);         comments = db.getCommentsByPostID(currentPostID);          commentsAdapter = new CommentsAdapter(this, comments);         rvComments.setAdapter(commentsAdapter);         rvComments.setOverScrollMode(View.OVER_SCROLL_NEVER);         rvComments.setOnScrollListener(new RecyclerView.OnScrollListener() {             @Override             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                 if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {                     commentsAdapter.setAnimationsLocked(true);                 }             }         });     }      private void setupSendCommentButton() {         btnSendComment.setOnSendClickListener(this);     }      private void startIntroAnimation() {         ViewCompat.setElevation(getToolbar(), 0);         contentRoot.setScaleY(0.1f);         contentRoot.setPivotY(drawingStartLocation);         llAddComment.setTranslationY(200);          contentRoot.animate()                 .scaleY(1)                 .setDuration(200)                 .setInterpolator(new AccelerateInterpolator())                 .setListener(new AnimatorListenerAdapter() {                     @Override                     public void onAnimationEnd(Animator animation) {                         ViewCompat.setElevation(getToolbar(), Utils.dpToPx(8));                         animateContent();                     }                 })                 .start();     }      private void animateContent() {         commentsAdapter.updateItems();         llAddComment.animate().translationY(0)                 .setInterpolator(new DecelerateInterpolator())                 .setDuration(200)                 .start();     }      @Override     public void onBackPressed() {         ViewCompat.setElevation(getToolbar(), 0);          contentRoot.postDelayed(new Runnable() {             @Override             public void run() {                 contentRoot.animate()                         .translationY(Utils.getScreenHeight(CommentsActivity.this))                         .setDuration(200)                         .setListener(new AnimatorListenerAdapter() {                             @Override                             public void onAnimationEnd(Animator animation) {                                 CommentsActivity.super.onBackPressed();                                 overridePendingTransition(0, 0);                             }                         })                         .start();             }         }, 100);     }      @Override     public void onSendClickListener(View v) {         if (validateComment()) {             publishComment(etComment.getText().toString());              PostResponse.PostComments newComment = new PostResponse().new PostComments();             newComment.user_id = Integer.parseInt(preferencesManager.getUserID());             newComment.comment = etComment.getText().toString();             newComment.post_id = currentPostID;              commentsAdapter.addItem(newComment);              commentsAdapter.setAnimationsLocked(false);             commentsAdapter.setDelayEnterAnimation(true);             rvComments.smoothScrollBy(0, rvComments.getChildAt(0).getHeight() * commentsAdapter.getItemCount());              etComment.setText(null);             btnSendComment.setCurrentState(SendCommentButton.STATE_DONE);         }     }       private boolean validateComment() {         if (TextUtils.isEmpty(etComment.getText())) {             btnSendComment.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake_error));             return false;         }          return true;     }      private Toolbar getToolbar(){         return toolbar;     }        /************************************************      * VOLLEY CALLS TO PUBLISH A COMMENT      * *****************************************************/     private void publishComment(final String comment) {        //Code here      }  } 

This is my adapter

public class CommentsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {      private Context context;     private int lastAnimatedPosition = -1;     private int avatarSize;     private int itemsCount = 0;       private boolean animationsLocked = false;     private boolean delayEnterAnimation = true;     List<PostResponse.PostComments> mItems;      public CommentsAdapter(Context context, List<PostResponse.PostComments> mItems) {         this.context = context;         this.mItems = mItems;         avatarSize = context.getResources().getDimensionPixelSize(R.dimen.comment_avatar_size);     }      @Override     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {         final View view = LayoutInflater.from(context).inflate(R.layout.item_comment, parent, false);         return new CommentViewHolder(view);     }      @Override     public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {         runEnterAnimation(viewHolder.itemView, position);         CommentViewHolder holder = (CommentViewHolder) viewHolder;         holder.tvComment.setText(mItems.get(position).comment);          Picasso.with(context)                 .load(R.drawable.user_profile)                 .centerCrop()                 .resize(avatarSize, avatarSize)                 .transform(new RoundedTransformation())                 .into(holder.ivUserAvatar);     }      private void runEnterAnimation(View view, int position) {         if (animationsLocked) return;          if (position > lastAnimatedPosition) {             lastAnimatedPosition = position;             view.setTranslationY(100);             view.setAlpha(0.f);             view.animate()                     .translationY(0).alpha(1.f)                     .setStartDelay(delayEnterAnimation ? 20 * (position) : 0)                     .setInterpolator(new DecelerateInterpolator(2.f))                     .setDuration(300)                     .setListener(new AnimatorListenerAdapter() {                         @Override                         public void onAnimationEnd(Animator animation) {                             animationsLocked = true;                         }                     })                     .start();         }     }      @Override     public int getItemCount() {         return mItems.size();     }      public void updateItems() {         notifyDataSetChanged();     }      public void addItem(PostResponse.PostComments newComment) {         mItems.add(newComment);         notifyItemInserted(mItems.size() - 1);     }      public void setAnimationsLocked(boolean animationsLocked) {         this.animationsLocked = animationsLocked;     }      public void setDelayEnterAnimation(boolean delayEnterAnimation) {         this.delayEnterAnimation = delayEnterAnimation;     }      public static class CommentViewHolder extends RecyclerView.ViewHolder {         @Bind(R.id.ivUserAvatar)         ImageView ivUserAvatar;         @Bind(R.id.tvComment)         TextView tvComment;          public CommentViewHolder(View view) {             super(view);             ButterKnife.bind(this, view);         }     } } 

Here is the screens.

enter image description here Working Well

enter image description here

The first works well but adding the first comment never works. I get this exception.

// Here is the exception

java.lang.NullPointerException at com.test.abc.CommentsActivity.onSendClickListener(CommentsActivity.java:214) at com.test.abc.views.SendCommentButton.onClick(SendCommentButton.java:80) at android.view.View.performClick(View.java:4463) at android.view.View$PerformClick.run(View.java:18770) at android.os.Handler.handleCallback(Handler.java:808) at android.os.Handler.dispatchMessage(Handler.java:103) at android.os.Looper.loop(Looper.java:193) 

1 Answers

Answers 1

I know you know it is because of this line -(you told me in comments)

rvComments.smoothScrollBy(0, rvComments.getChildAt(0).getHeight() *           commentsAdapter.getItemCount()); 

but did you know it is the method call? rvComments.getChildAt(0).getHeight() ?

my thoughts is, if the recyleView is empty calling rvComments.getChildAt(0) is a reference to Null Reference, its empty hence you NPE but if the first comment is already added you have list of size 1 so this line rvComments.getChildAt(0).getHeight() actaully reference a View Object address.

So i guess your solution is _since you know the preferred height of it View or pick a preferred height and hardcode it and use. Or if you really insist of using the View height then check the list size before you advance.

...   commentsAdapter.setAnimationsLocked(false);   commentsAdapter.setDelayEnterAnimation(true);   if(rvComments.getChildCount() > 0){   //i guess you will have do rvComments.getChildAt(   //rvComments.getChildCount()-1) rather than below   rvComments.smoothScrollBy(0, rvComments.getChildAt(0).getHeight()             * commentsAdapter.getItemCount());   }else{   //do something else or find something to do to get the height you want   //and multiply. In short it means the first item. ... 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment