Max Zavernutiy
Max Zavernutiy

Reputation: 1879

ListView changes and removes views on scroll

I'm using ParseQueryAdapter to load data into my listview. I'm using listview for some kind of chat. But when I scroll the ListView it completely messes up the layout. Here is the screenshot how the chat look from the very beginning, just after loading all data: Chat from the begininng

And this is how it looks after I scroll it up and down few times: Chat after scroll

I have already seen the similar problems and answers like: "User the ViewHolder pattern" But I was using it from the very beginning and this didn't help. Also I made a width and height of ListView match_parent, because I saw that wrap content messes up gettin the view item.

I assume that my problem can be in the AsyncTask, because it's where I set up the ListView item. Here is the code of the adapter:

public class ChatParseQueryAdapter extends ParseQueryAdapter<Message> {
    Activity activity;

    String lighterID;
    String lighterColor;

    public ChatParseQueryAdapter(Context context, final String lighterID, String lighterColor){
        super(context, new QueryFactory<Message>() {
            @Override
            public ParseQuery<Message> create() {
                ParseQuery<Message> query = new ParseQuery<>("Message");
                query.orderByAscending("createdAt");
                query.whereEqualTo("lighter", ParseObject.createWithoutData("Lighter", lighterID));
                query.include("user");
                return query;
            }
        });
        activity = (Activity) context;

        this.lighterID = lighterID;
        this.lighterColor = lighterColor;
    }

    @Override
    public View getItemView(Message message, View v, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (v == null){
            v = View.inflate(getContext(), R.layout.message_list_item, null);

            viewHolder = new ViewHolder();

            viewHolder.messageDate = (TextView) v.findViewById(R.id.message_date);
            viewHolder.otherUserImage = (ImageView) v.findViewById(R.id.message_other_user_image);
            viewHolder.otherUserName = (TextView) v.findViewById(R.id.message_other_user_name);
            viewHolder.messageTextLayout = (RelativeLayout) v.findViewById(R.id.message_text_layout);
            viewHolder.messageText = (TextView) v.findViewById(R.id.message_text);
            viewHolder.messageImage = (ImageView) v.findViewById(R.id.message_picture);
            viewHolder.userImage = (ImageView) v.findViewById(R.id.message_user_image);
            viewHolder.userName = (TextView) v.findViewById(R.id.message_user_name);

            v.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) v.getTag();
        }

        new GetMessage(viewHolder).execute(message);



        return super.getItemView(message, v, parent);
    }

    static class ViewHolder {
        TextView messageDate;
        ImageView otherUserImage;
        TextView otherUserName;
        RelativeLayout messageTextLayout;
        TextView messageText;
        ImageView messageImage;
        ImageView userImage;
        TextView userName;
    }

    class GetMessage extends AsyncTask<Message, Void, ChatMessage> {
        ViewHolder viewHolder;
        Resources resources;

        public GetMessage(ViewHolder viewHolder){
            this.viewHolder = new ViewHolder();
            this.viewHolder = viewHolder;

            resources = activity.getResources();
        }

        @Override
        protected ChatMessage doInBackground(Message... params) {
            if (isCancelled()){
                cancel(true);
                return null;
            }
            Message message = params[0];
            ChatMessage result = new ChatMessage();

            ParseUser messageAuthor = message.getUser();
            String userProfileID = null;
            String userName = null;
            if (messageAuthor != null) {

                userProfileID = messageAuthor.getString("profileID");
                userName = messageAuthor.getString("name").toUpperCase();

            }
            result.setIsOtherMessage(messageAuthor != AppDelegate.getUser());

            Bitmap bitmap = GraphicsHelper.getBitmap(userProfileID, messageAuthor, activity);
            result.setUserImage(GraphicsHelper.getRoundedBitmap(bitmap));
            result.setUserName(userName);
            String messageText = message.getContent();
            if (messageText != null){
                result.setMessage(messageText);
            } else {
                result.setMessageImage(getParseImage(message));
            }


            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yy");
            String date = simpleDateFormat.format(Date.parse(message.getCreatedAt().toString()));
            result.setDate(date);

            return result;
        }

        @Override
        protected void onPostExecute(ChatMessage result) {
            super.onPostExecute(result);

            if (result != null && !isCancelled()){
                if (result.isOtherMessage()){
                    viewHolder.userImage.setVisibility(View.GONE);
                    viewHolder.userName.setVisibility(View.GONE);

//                    Drawable drawable = resources.getDrawable(R.drawable.black_other_chat);
                    viewHolder.messageTextLayout.setBackgroundResource(R.drawable.black_other_chat);
//                    viewHolder.messageTextLayout.setBackground(drawable);


                    viewHolder.otherUserImage.setImageBitmap(result.getUserImage());
                    viewHolder.otherUserName.setText(result.getUserName());
                } else {
                    viewHolder.otherUserImage.setVisibility(View.GONE);
                    viewHolder.otherUserName.setVisibility(View.GONE);

//                    Drawable drawable = activity.getResources().getDrawable(R.drawable.black_my_chat);
                    viewHolder.messageTextLayout.setBackgroundResource(R.drawable.black_my_chat);
//                    viewHolder.messageTextLayout.setBackground(drawable);

                    viewHolder.userImage.setImageBitmap(result.getUserImage());
                    viewHolder.userName.setText(result.getUserName());
                    viewHolder.messageText.setTextColor(GraphicsHelper.getAnotherColor(lighterColor, activity.getResources()));
                }
                String messageText = result.getMessage();
                if (messageText != null){
                    viewHolder.messageDate.setText(result.getDate());
                    viewHolder.messageImage.setVisibility(View.GONE);
                    viewHolder.messageText.setText(result.getMessage());
                } else {
                    viewHolder.messageDate.setVisibility(View.GONE);
                    viewHolder.messageText.setVisibility(View.GONE);
                    viewHolder.messageImage.setImageBitmap(result.getMessageImage());
                }
            }
        }

        private Bitmap getParseImage (Message message){
            Bitmap bitmap = null;
            byte[] imageByteArray;
            if (message != null){
                ParseFile imageFile = (ParseFile) message.get("info");
                try {
                    imageByteArray = imageFile.getData();
                    bitmap = BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length);
//                    int dimension = GraphicsHelper.getSquareCropDimensionForBitmap(bitmap);
//                    bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
                } catch (ParseException | NullPointerException e1) {
//                e1.printStackTrace();
                }
            }
            return bitmap;
        }
    }
}

This is my chat fragment code:

public class FragmentChat extends Fragment {
    String lighterID;
    String lighterColor;

    ListView chatListView;
    View globalView;

    ChatParseQueryAdapter mainAdapter;
    ApplicationInterface fragmentHolder;

    public FragmentChat(String lighterID, String lighterColor){
        this.lighterID = lighterID;
        this.lighterColor = lighterColor;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            fragmentHolder = (ApplicationInterface) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString());
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_chat, container, false);

        globalView = view;
        new SetUpChatTitle(view).execute(0);

        chatListView = (ListView) view.findViewById(R.id.chat_list_view);

//        chatListView.setOnScrollListener(new AbsListView.OnScrollListener() {
//            @Override
//            public void onScrollStateChanged(AbsListView view, int scrollState) {
//
//            }
//
//            @Override
//            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//
//            }
//        });

        mainAdapter = new ChatParseQueryAdapter(getActivity(), lighterID, lighterColor);
        chatListView.setAdapter(mainAdapter);


        RelativeLayout chatLayout = (RelativeLayout) view.findViewById(R.id.chat_layout);
        chatLayout.setBackgroundColor(GraphicsHelper.getColor(lighterColor, getResources()));

//        EditText chatInputMessage = (EditText) view.findViewById(R.id.chat_input_message);
//        chatInputMessage.setBackgroundResource(R.drawable.round_corners_layout);
//        GradientDrawable inputMessageDrawable = (GradientDrawable) chatInputMessage.getBackground();
//        inputMessageDrawable.setColor(getResources().getColor(R.color.white));
//        inputMessageDrawable.setStroke(4, GraphicsHelper.getAnotherColor(lighterColor, resources));
//        inputMessageDrawable.setCornerRadius(40);
//
//        Button sendMessageButton = (Button) view.findViewById(R.id.chat_send_message);
//        sendMessageButton.setBackgroundResource(R.drawable.round_corners_layout);
//        GradientDrawable sendMessageDrawable = (GradientDrawable) sendMessageButton.getBackground();
//        sendMessageDrawable.setColor(GraphicsHelper.getAnotherColor(lighterColor, resources));
//        sendMessageDrawable.setCornerRadius(30);

        return view;
    }

    class SetUpChatTitle extends AsyncTask<Integer, Void, RankingItem> {
        View view;

        HorizontalScrollView chatTitleScrollView;
        RelativeLayout lighterNameAndMilesLayout;
        TextView rank;
        TextView lighterName;
        TextView milesTravelled;
        ImageView userImage;

        LinearLayout enterMessageLayout;
        EditText chatInputMessage;
        Button sendMessageButton;
        ImageView attachPictureButton;

        public SetUpChatTitle(View view){
            this.view = view;

            chatTitleScrollView = (HorizontalScrollView) view.findViewById(R.id.chat_title_scroll_view);
            lighterNameAndMilesLayout = (RelativeLayout) view.findViewById(R.id.chat_lighter_name_and_miles_layout);
            rank = (TextView) view.findViewById(R.id.chat_rank_number);
            lighterName = (TextView) view.findViewById(R.id.chat_lighter_name);
            milesTravelled = (TextView) view.findViewById(R.id.chat_lighter_miles_travelled);
            userImage = (ImageView) view.findViewById(R.id.chat_user_image);

            enterMessageLayout = (LinearLayout) view.findViewById(R.id.chat_message_layout);
            chatInputMessage = (EditText) view.findViewById(R.id.chat_input_message);
            sendMessageButton = (Button) view.findViewById(R.id.chat_send_message);
            attachPictureButton = (ImageView) view.findViewById(R.id.chat_attach_picture_button);
        }

        @Override
        protected RankingItem doInBackground(Integer... params) {
            if (isCancelled()){
                cancel(true);
                return null;
            }

            RankingItem result = new RankingItem();

            ParseQuery<ParseObject> lighterQuery = ParseQuery.getQuery("Lighter");
            try {
                Lighter lighter = (Lighter) lighterQuery.get(lighterID);
                result.setLighterName(lighter.getLighterName().toUpperCase());
                result.setMilesTravelled(String.valueOf(lighter.getIntMiles()) + " MILES TRAVELLED");
                ParseUser lastUser = lighter.getMaster();
                String profileID = null;
                if (lastUser != null) {
                    lastUser.fetchIfNeeded();
                    profileID = lastUser.getString("profileID");
                }
                Bitmap bitmap = GraphicsHelper.getBitmap(profileID, lastUser, getActivity());
                result.setUserImage(GraphicsHelper.getBitmapWithTineCircle(bitmap, getActivity()));

                lighterQuery.whereGreaterThanOrEqualTo("miles", lighter.getDouble("miles"));
                result.setRank("#" + lighterQuery.count());

                JSONArray userList = lighter.getJSONArray("users");

                String parseUserObjectId = AppDelegate.getUser().getObjectId();
                boolean userChat = false;
                for (int i = 0; i < userList.length(); i++){
                    if (userList.getJSONObject(i).getString("objectId").equals(parseUserObjectId)) {
                        userChat = true;
                    }
                }
                result.setUserChat(userChat);
            } catch (ParseException | JSONException e) {
                e.printStackTrace();
            }

            return result;
        }

        @Override
        protected void onPostExecute(RankingItem result) {
            super.onPostExecute(result);

            if (result != null && !isCancelled()){
                GraphicsHelper.setUpChatTitle(view, lighterColor, getActivity());

                lighterNameAndMilesLayout.getLayoutParams().width = GraphicsHelper.getScreenSize(getActivity()).x - FragmentMap.MARGIN;
                chatTitleScrollView.setSmoothScrollingEnabled(true);
                globalView.post(new Runnable() {
                    @Override
                    public void run() {
                        chatTitleScrollView.fullScroll(View.FOCUS_RIGHT);
                    }
                });

                rank.setText(result.getRank());
                lighterName.setText(result.getLighterName());
                milesTravelled.setText(result.getMilesTravelled());
                userImage.setImageBitmap(result.getUserImage());

                final GestureDetector gestureDetector = new GestureDetector(
                        new GestureDetectorTwoPositions(1, chatTitleScrollView));

                chatTitleScrollView.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return gestureDetector.onTouchEvent(event);
                    }
                });

                chatTitleScrollView.setVisibility(View.VISIBLE);

                if (result.isUserChat()){
                    Resources resources = getResources();

                    chatInputMessage.setBackgroundResource(R.drawable.round_corners_layout);
                    GradientDrawable inputMessageDrawable = (GradientDrawable) chatInputMessage.getBackground();
                    inputMessageDrawable.setColor(getResources().getColor(R.color.white));
                    inputMessageDrawable.setStroke(4, GraphicsHelper.getAnotherColor(lighterColor, resources));
                    inputMessageDrawable.setCornerRadius(40);

                    sendMessageButton.setBackgroundResource(R.drawable.round_corners_layout);
                    GradientDrawable sendMessageDrawable = (GradientDrawable) sendMessageButton.getBackground();
                    sendMessageDrawable.setColor(GraphicsHelper.getAnotherColor(lighterColor, resources));
                    sendMessageDrawable.setCornerRadius(30);

                    sendMessageButton.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            String inputMessage = chatInputMessage.getText().toString().trim();
                            if (TextUtils.isEmpty(inputMessage)) {
                                LoginActivity.showAlertDialog(getString(R.string.enter_the_message), getActivity());
                            } else {
                                Message message = new Message();

                                message.setUser(AppDelegate.getUser());
                                message.setPublicReadWriteAcl();
                                message.setLighter(ParseObject.createWithoutData("Lighter", lighterID));

                                Location currentLocation = LocationInfo.getBestLocation(getActivity());
                                ParseGeoPoint location = new ParseGeoPoint(currentLocation.getLatitude(), currentLocation.getLongitude());
                                message.setLocation(location);

                                message.setContent(inputMessage);
                                message.setType(1d);
                                message.saveInBackground(new SaveCallback() {
                                    @Override
                                    public void done(ParseException e) {
                                        mainAdapter.notifyDataSetChanged();
                                        chatListView.setAdapter(mainAdapter);
                                    }
                                });
                            }
                        }
                    });

                    enterMessageLayout.setVisibility(View.VISIBLE);
                    attachPictureButton.setVisibility(View.VISIBLE);
                }
            }
        }

        public void receiveMessage(){

        }
    }
}

And this is how my ListView xml code looks like:

<ListView
    android:id="@+id/chat_list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:dividerHeight="0dp"
    android:divider="@null"
    android:stackFromBottom="true"
    android:transcriptMode="alwaysScroll"
    android:layout_marginBottom="@dimen/double_margin"
    android:layout_below="@id/chat_title_scroll_view"
    android:layout_above="@+id/chat_message_layout"/>

Thank you in advance.

Upvotes: 0

Views: 480

Answers (1)

Rahul Sharma
Rahul Sharma

Reputation: 6179

Try this: Override these two methods in your adapter class:

    @Override
  public int getViewTypeCount() {

   return 1;
  }

  @Override
  public int getItemViewType(int position) {

   return position;
  }

you can also see this: List view items changes position when scrolling android?

Edited Adater class's getItemView() method called again and again very frequently, and you are calling your asyncTask(which runs slow) from this method, i.e. not good approach. you need to call asyncTask from your fragment class then call the adapter class from onPostExecute() of your asynkTask.

Upvotes: 1

Related Questions