0

I have a list view that when scrolling, the item in the middle of the list view is enlarged and its color changes. But I have a problem, and it crashes when I scroll fast. My code is located below. Can anyone tell me what is causing the problem? Thanks

myActivity.java

public class myActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_entekhb_zaman);


       txtday=(TextView)findViewById(R.id.txtday);
       txtdayok=(TextView)findViewById(R.id.txtdayok);
       listviewday = (ListView) findViewById(R.id.listday);

       listday.add("");
       listday.add("1");
       listday.add("2");
       listday.add("3");
       listday.add("4");
       listday.add("");

       adapterday=new Custom_List_Day(this,android.R.layout.simple_list_item_1,listday);
       listviewday.setAdapter(adapterday);


        listviewday.setOnScrollListener(new AbsListView.OnScrollListener() {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            // TODO Auto-generated method stub
            if (scrollState == SCROLL_STATE_IDLE) {
                View itemView = view.getChildAt(0);
                int top = Math.abs(itemView.getTop());
                int bottom = Math.abs(itemView.getBottom());
                int scrollBy = top >= bottom ? bottom : -top;
                if (scrollBy == 0) {
                    return;
                }
                smoothScrollDeferred(scrollBy, (ListView)view);
            }
        }

        private void smoothScrollDeferred(final int scrollByF,
                                          final ListView viewF) {
            final Handler h = new Handler();
            h.post(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    viewF.smoothScrollBy(scrollByF, 200);
                }
            });
        }

        @Override
        public void onScroll(AbsListView arg0, int firstVisibleItem, int visibleItemCount, int totalItemCount){
            Log.i("Scroll","first "+firstVisibleItem+", visibleItemCount "+visibleItemCount+",totalCount "+totalItemCount);
            int center = firstVisibleItem+(visibleItemCount)/2 ;
            if(currentLargedPosition != center){
                enlargeMiddleView(currentLargedPosition-firstVisibleItem, center-firstVisibleItem);
                currentLargedPosition = center;
                txtdayok.setText(listday.get(currentLargedPosition));
            }

        }
    });


     }

void enlargeMiddleView(int oldPosition, int newPosition){

    // get enlarged view and make it return default size
    TextView newTextView = (TextView)listviewday.getChildAt(oldPosition).findViewById(R.id.txtday3);
    newTextView.setTextSize(22);
    newTextView.setTextColor(getResources().getColor(R.color.white));


    // get the current center view and make it bigger
    TextView oldTextView = (TextView)listviewday.getChildAt(newPosition).findViewById(R.id.txtday3);
    oldTextView.setTextSize(28);
    oldTextView.setTextColor(getResources().getColor(R.color.sormeii));
}
}

Custom_List_Day

public class Custom_List_Day extends ArrayAdapter<String> {

private final Activity context;
private final ArrayList<String> name;
private final int resource;


public Custom_List_Day(Activity context,int resource, ArrayList<String> name) {
    super(context, resource, name);

    this.context = context;
    this.name = name ;
    this.resource=resource;


}
@Override
public int getCount() {
    // TODO Auto-generated method stub
    return name.size();
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return position;
}

@Override
public int getViewTypeCount() {
    return super.getViewTypeCount();
}



public View getView(int position, View convertView, ViewGroup parent) {


    LayoutInflater infalter = context.getLayoutInflater();


    View rowLayout = null;




    if (convertView == null) {
        // inflating the row

        rowLayout =  infalter.inflate(this.resource, parent,false);


    } else {
        rowLayout = convertView;
    }


    TextView txtName = (TextView) rowLayout.findViewById(R.id.txtday3);

    txtName.setText(name.get(position));

    return rowLayout ;
}
}

list_day.xml

  <LinearLayout android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@color/colorPrimary"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:padding="4dp">


    <TextView
        android:id="@+id/txtday3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="شنبه - ۹خرداد"
        android:textColor="@color/white"
        android:textSize="22sp"/>

     </LinearLayout>

When I slowly scroll, I have no problems and the program works. But when I scroll fast, the program crashes. logcat error

07-15 12:32:45.607 14920-14920/anjam.carno E/AndroidRuntime: FATAL EXCEPTION: main
                                                         Process: anjam.carno, PID: 14920
                                                         java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
                                                             at anjam.carno.ActivityEntekhabZaman.enlargeMiddleView2(ActivityEntekhabZaman.java:232)
                                                             at anjam.carno.ActivityEntekhabZaman$2.onScroll(ActivityEntekhabZaman.java:189)
                                                             at android.widget.AbsListView.invokeOnItemScrollListener(AbsListView.java:1519)
                                                             at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5245)
                                                             at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:4668)
                                                             at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
                                                             at android.view.Choreographer.doCallbacks(Choreographer.java:580)
                                                             at android.view.Choreographer.doFrame(Choreographer.java:549)
                                                             at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
                                                             at android.os.Handler.handleCallback(Handler.java:739)
                                                             at android.os.Handler.dispatchMessage(Handler.java:95)
                                                             at android.os.Looper.loop(Looper.java:135)
                                                             at android.app.ActivityThread.main(ActivityThread.java:5910)
                                                             at java.lang.reflect.Method.invoke(Native Method)
                                                             at java.lang.reflect.Method.invoke(Method.java:372)
                                                             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1405)
                                                             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1200)
Firouziam
  • 777
  • 1
  • 9
  • 31
  • 1
    please post your crash report – Muhammad Umar Jul 15 '18 at 07:52
  • You need to make sure that `onScroll` fires only after the `handler` finishes. In case of slow scroll, it happens automatically. When scrolling fast, you need to introduce some logic for that purpose. – Susmit Agrawal Jul 15 '18 at 07:58
  • @MuhammadUmar I edited the codes. – mohsen khorasani Jul 15 '18 at 08:06
  • @SusmitAgrawal Can you give more guidance? – mohsen khorasani Jul 15 '18 at 08:07
  • Why exactly do you need the delay of `200` in the handler? – Susmit Agrawal Jul 15 '18 at 08:16
  • @SusmitAgrawal For each item in the middle, the information is loaded from the server. And in order for this information to have enough time to load, I set the value to 200. Is it wrong? – mohsen khorasani Jul 15 '18 at 08:20
  • i was thinking if it could be done this way: like the gmail app, let the list be updated with the data when the user, say, swipes down on the list. until then the list would display the current data it has. this way the list adapter always has the data ready to display – hrushi Jul 15 '18 at 08:45
  • @hrushi In this app, at first some data is read from the server and placed in a list, and then by placing each of the data in the middle of the other information, the middle item is read from the server and placed in the next list. And the data is always changed. I do not think it can be done like Gmail. – mohsen khorasani Jul 15 '18 at 08:53
  • @mohsenkhorasani put a check in your enlargeMiddleView, your oldPosition and newPosition should be less than listView items size. – Muhammad Umar Jul 15 '18 at 10:45
  • @MuhammadUmar Thanks but it did not work. Do not have another idea? – mohsen khorasani Jul 15 '18 at 12:12
  • @mohsenkhorasani i suspect on fast scroll listView holder is not initialised for that position hence the child is null. I am not sure what are you trying to achieve from above code. a little explanation will help – Muhammad Umar Jul 15 '18 at 12:20
  • @MuhammadUmar The problem is that I can not define the holder for this list view. How many times did it, but when the scroll was done very quickly, it crashed again. Tell me about the explanation where I want to explain – mohsen khorasani Jul 15 '18 at 12:41
  • Just tell us what are you trying to achieve via scroll @mohsenkhorasani – Muhammad Umar Jul 15 '18 at 13:37
  • @MuhammadUmar I want to display the item in the middle of the bigger list view, and these code work fine, but when I scroll very quickly, the program crashes. I just want the scroll to display the middle item bigger in the quick scroll – mohsen khorasani Jul 15 '18 at 14:41

2 Answers2

1

Firstly, I would like to suggest you to read Standing coding guidelines for android. It will improve your coding standards.

Secondly you need to remove the handler. Judging from your code, you want to snap the ListView to middle. Please have a look at RecyclerView. If you want to add snap to middle this link Linear Snap Helper will help you.

However if you want to modify your code I suggest you do following changes

    listviewday.setOnScrollListener(new AbsListView.OnScrollListener()
    {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState)
        {
            // TODO Auto-generated method stub
            if (scrollState == SCROLL_STATE_IDLE)
            {
                View itemView = view.getChildAt(0);
                int top = Math.abs(itemView.getTop());
                int bottom = Math.abs(itemView.getBottom());
                int scrollBy = top >= bottom ? bottom : -top;
                if (scrollBy == 0)
                {
                    return;
                }

                view.smoothScrollBy(scrollBy, 200);
            }
        }

        @Override
        public void onScroll(AbsListView arg0, int firstVisibleItem, int visibleItemCount, int totalItemCount)
        {
            if (visibleItemCount != 0)
            {
                if (oldTextView != null)
                {
                    oldTextView.setTextSize(22);
                    oldTextView.setTextColor(Color.WHITE);
                    oldTextView = null;
                }

                final int midPosition = visibleItemCount - (visibleItemCount / 2);
                final TextView listItem = (TextView) listviewday.getChildAt(midPosition - 1).findViewById(R.id.txtday3);
                listItem.setTextSize(26);
                listItem.setTextColor(Color.BLACK);

                oldTextView = listItem;
            }

        }
    });
Muhammad Umar
  • 11,391
  • 21
  • 91
  • 193
0

IMO, the 200ms delay is what's causing the crash. Off the top of my head,

Change

if(currentLargedPosition != center){
    enlargeMiddleView(currentLargedPosition-firstVisibleItem, center-firstVisibleItem);
    currentLargedPosition = center;
    txtdayok.setText(listday.get(currentLargedPosition));
}

in onScroll to

new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(210);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if(currentLargedPosition != center){
                            enlargeMiddleView(currentLargedPosition-firstVisibleItem, center-firstVisibleItem);
                            currentLargedPosition = center;
                            txtdayok.setText(listday.get(currentLargedPosition));
                        }
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();

Of course, it will appear as if the app is lagging while scrolling. But this is the only way I can think of, without changing your core logic. Also, this is just a theory, and I'm not 100% sure it will work.

Susmit Agrawal
  • 3,649
  • 2
  • 13
  • 29
  • English, please. – Susmit Agrawal Jul 15 '18 at 08:32
  • Excuse me from the mistake Thanks but it did not work. So, in your opinion, is the problem of 200? – mohsen khorasani Jul 15 '18 at 08:34
  • Yes. I think when you scroll slowly, there is enough time for the handler to finish executing. The `onScroll` then has some data to work on, since the view has finished scrolling. This does not happen when you scroll quickly. Multiple calls to `onScroll` are issued before you finishing processing a single element. – Susmit Agrawal Jul 15 '18 at 08:38
  • Thank you very much, but I deleted the smoothScrollDeferred and even reduced the amount of 200 to 1, but it seems like a mistake from another place in the findview. – mohsen khorasani Jul 15 '18 at 08:46
  • Yes, but any other way would require changes in the core logic. – Susmit Agrawal Jul 15 '18 at 14:03