7

I have a ViewPager and a GridView to show a monthly calendar. The GridView has about 30 elements and swipe to left and right is very slow. I made a sample project to test without any database access and it is slow, too. Maybe someone sees the problem or do I have to make an asynchronous load of the pages?

That is the layout for one element in the GridView. I want to show 9 little 5x5 pixel icons in any element, but without them, it is slow, too.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/Layout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:padding="1dp">
  <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content">
    <TextView android:id="@+id/date" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginLeft="5dp" android:gravity="center_vertical" android:textSize="14sp" android:textStyle="bold"/>
  </LinearLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot1" android:layout_width="wrap_content" android:drawingCacheQuality="auto" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:background="#ffffff" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:paddingBottom="1dp" android:paddingLeft="1dp" android:paddingTop="5dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="right" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_gravity="center_horizontal" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_gravity="left" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="left" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="left" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_gravity="left" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
    <ImageView android:id="@+id/dot9" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="left" android:background="#ffffff" android:paddingBottom="5dp" android:paddingLeft="1dp"/>
  </RelativeLayout>
</LinearLayout>

That's the instantiateItem of GridView adapter:

public Object instantiateItem(ViewGroup container, int position) {
    GridView gv = new GridView(m_context);
    gv.setAdapter(new BaseAdapter() {
        @Override
        public View getView(int pos, View convertView, ViewGroup parent) {
            View v;
            if (convertView == null) {
                // if it's not recycled, initialize some attributes
                v = mInflater.inflate(R.layout.calendar_month_item, null);
            } else {
                v = convertView;
            }
            TextView dayView = (TextView) v.findViewById(R.id.date);
            dayView.setText(pos + "");
            return v;
        }

        @Override
        public long getItemId(int p) {
            return 0;
        }

        @Override
        public Object getItem(int p) {
            return null;
        }

        @Override
        public int getCount() {
            return 30;
        }
    });
    gv.setNumColumns(5);
    ((ViewPager) container).addView(gv);
    return gv;
}
daxim
  • 39,270
  • 4
  • 65
  • 132
MOST2K2
  • 255
  • 4
  • 14
  • 4
    Use Traceview to identify where your bottlenecks lie. – CommonsWare Jun 13 '12 at 17:49
  • I looked into TraceView but i cant see where the problem is, its only UI representation. – MOST2K2 Jun 14 '12 at 05:50
  • 2
    You are wrapping you ImageViews inside RelativeLayouts and your TextView inside a LinearLayout: This completely unnecessary. Second, you have multiple GridViews... Do you have multiple Adapters? you do not need all the adapters. You can manage everything in one adapter! Finally, just contemplating, you do not need a ViewPager. With simple animations you can make this work with a single GridView (and a single adapter reset each time). Good luck – Sherif elKhatib Jun 18 '12 at 12:22
  • i wrap the imageview in a relativelayout because i wanted to allign the icon at the left side. no i have only one gridview (ok one for each page) but only one adapter which i register in oncreate of course.the viewpager has some nice animation that you can see the contents while swiping to left or right. but sure could have used an animation for that. – MOST2K2 Jun 19 '12 at 04:58
  • i think i got it to work. i removed the gridview and i inflate a simple TableLayout programmatically for the whole month. the icons will be drawn with a custom painted canvas (one canvas a day only). now swiping to left and right is fast enough. every answer from you gave me a little hint what i can do better. thx! – MOST2K2 Jun 19 '12 at 05:09
  • If you can, don't forget to award the bounty to CommonKnowledge. :) – Mikael Ohlson Jun 19 '12 at 19:09

3 Answers3

5

I looked around for a bit and I think I found exactly what you are looking for:

daxim
  • 39,270
  • 4
  • 65
  • 132
CommonKnowledge
  • 769
  • 1
  • 10
  • 35
  • Yes i know this project. but i need 9 icons (and they have enabled/disabled state) in one item of the gridview, so i add them programmatically at the moment. maybe i put them in the layout and show up the needed ones and hide the others. i will post again if that speeds up the viewpager with the gridview. – MOST2K2 Jun 14 '12 at 06:15
  • I have looked this over again and I think you should stick with what I said before. Although instead of displaying all of these icons in the "month view" of the calendar, you should only display one icon if that day contains any icons. Then when you select the day in the "month view" it will pop up a dialog of that day that contains all of the icons that the user added to that day. Its easier cleaner and works a lot faster. Also you can try using a SQLite DB to store all of the days that have icons and what icons they contain. You will then have many options like a 'to do list' or whatever. – CommonKnowledge Jun 14 '12 at 22:46
  • @MOST2K2 In what I described above you can used a custom Dialog and it will have its own xml file. – CommonKnowledge Jun 14 '12 at 23:31
  • Thanks for looking again for my little problem ;) – MOST2K2 Jun 15 '12 at 05:51
  • but i want to use all 9 icons in the day view, every icon is for one hour in a "school calendar" and if the user has put some information in the hour i will show up the "has information" icon, otherwise i show the disabled icon. on click at the day i show a popup window with details. but in my sample project to test the speed i have no db access and its slow, yes i know why its slow now. 30 views of gridview and within the layout with 10 rows (1 date row, 9 icon rows). the todo list is a good point, but it wont solve my problem. – MOST2K2 Jun 15 '12 at 05:59
  • I use no custom dialog in my activities. only custom layouts. i think about to replace the grieview with one big layout with all days a month and the 9 icons for one day. – MOST2K2 Jun 15 '12 at 06:17
2

Which version of Android do you use? This is important, because there is a Calendar in Android 3.0, is has bugs, but not so critical.

About your question, if I well understood it. Look at your XML, it contains 11 layouts, and this is just for only one element. If you have them many, imagine the amount of work the Android system must do, to inflate all your elements. And after that, when you swipe, you reuse the elements, and Android must refresh the ones that are not valid. This is a lot of work. (11 layouts * 30 = 330 layouts to be refreshed or inflated). As Android developer documentation says, you must always use as less layouts to wrap your elements, as possible!

Anyway, your approach is not correct. I can suggest you to look at the source code of the CalendarView in Android 3+, but to give you a hint:

Create a layout for a week, not for every day(like in CalendarView). By doing this, Android will have to refresh only n elements(You'll choose how many weeks you want to display at a moment of time), not 30. This layout must contain 7 views for every day.

Hope you get some idea from this.

XMight
  • 1,991
  • 2
  • 20
  • 36
  • I use Android 4.0.4 at my samsung galaxy nexus. but i want to run my app for api level > 8 (2.2.x), i dont use the internal calendar, its my own implementation. Yes thats true 330 layouts is a lot of work, and that is for every month. i have a week and day perspective, too. these are very fast while swiping to left or right. i try to reduce the layouts and i hope to get it working. maybe i change to fragments and do an ansynchronous load of the pages. – MOST2K2 Jun 14 '12 at 06:06
  • Why invent something if there already exists a calendar? For example I needed to draw some circles on every row of the android calendar. I downloaded the source code of this calendar, and changed some lines, and it works. I didn't test it on 2.2 though, only 3+. – XMight Jun 14 '12 at 15:04
  • i use my calendar for a class book for teachers, its very specific with only 1-10 hours. – MOST2K2 Jun 14 '12 at 18:33
0

First of all, your layout is a bit strange, as others have stated.

  • You really don't need the schema for any layout sub elements.
  • Having multiple nested relative or linear layouts when not needed will be quite expensive, especially when wrapped in a GridView wrapped in a ViewPager.

Second of all, the ViewPager and PagerAdapter are a little bit tricky.

  • When you have the default number of offscreen pages, you will layout all three of them at the same time. If you set it higher, you will be layouting even more views.
  • Make sure that you correctly remove views in destroyItem.
  • Implement the isViewFromObject method correctly, since otherwise your views will be discarded when you page, causing more cost of recreating them.

Going with @CommonsWare's advice is probably a good idea. TraceView is a bit difficult to use, but even so it will probably yield some clues.

Mikael Ohlson
  • 3,074
  • 1
  • 22
  • 28
  • yes thats true that i dont need such sub elements. i dont change the default offscreen pages of 3. remove and isViewFromObject is correctly implemented. – MOST2K2 Jun 19 '12 at 05:03