6

I'm developing an Activity with a ViewPager that contains Fragments (Max 4 fragment each time) when each containing TableLayout. So basically 4 Tables are loaded. This is the code for loading the data into the Fragment which is later attached to the ViewPager:

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{
    if (container == null) {
        return null;
    }

    LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    layoutParams.gravity=Gravity.CENTER_VERTICAL;
    layoutParams.setMargins(10, 0, 0, 0);

    fragmetLayout = (LinearLayout)inflater.inflate(R.layout.grid_fragment_layout, container, false);
    table = (TableLayout) fragmetLayout.findViewById(R.id.tlGridTable);
    llParameterContainer =  (LinearLayout) fragmetLayout.findViewById(R.id.llParametersContainer);
    tabName = (TextView) fragmetLayout.findViewById(R.id.tvTabName);
    tabName.setText(tab.getTabName());
    //tabName.setLayoutParams(layoutParams);

    for (Parameter parameter : SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository())
    {
        ImageView parameterPoint = new ImageView(getActivity());
        parameterPoint.setImageResource(R.drawable.parameter_chosen_point);
        parameterPoint.setPadding(10, 10, 10, 0);
        parameterPoint.setLayoutParams(layoutParams);
        llParameterContainer.addView(parameterPoint);

        TextView parameterTextView = new TextView(getActivity());
        parameterTextView.setText(parameter.getName()+":");
        parameterTextView.setGravity(Gravity.CENTER);
        parameterTextView.setTextColor(getResources().getColor(R.color.my_black));  
        parameterTextView.setPadding(5, 10, 3, 10);
        parameterTextView.setLayoutParams(layoutParams);
        llParameterContainer.addView(parameterTextView);
        TextView parameterChosenValueTextView = new TextView(getActivity());
        parameterChosenValueTextView.setText(parameter.getChosenValue());
        parameterChosenValueTextView.setGravity(Gravity.CENTER); 
        parameterChosenValueTextView.setPadding(0, 10, 0, 10);
        parameterChosenValueTextView.setLayoutParams(layoutParams);
        parameterChosenValueTextView.setTextColor(getResources().getColor(R.color.my_black));   
        llParameterContainer.addView(parameterChosenValueTextView);

    }
    //table.setStretchAllColumns(true);  
    //table.setShrinkAllColumns(true); 
    TableRow tableRow = new TableRow(getActivity());
    TableLayout.LayoutParams tableRowParams=new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT,TableLayout.LayoutParams.WRAP_CONTENT);
    tableRowParams.setMargins(2, 0, 0, 0);
    tableRow.setLayoutParams(tableRowParams);
    Log.d(TAG, "TAB FROM FRAGMENT:"+tab.toString());


    TableRow.LayoutParams tlparamsFirstColumn = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT); 
    tlparamsFirstColumn.setMargins(4, 0, 2, 0);
    TableRow.LayoutParams tlparams = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT); 
    tlparams.setMargins(0, 0, 2, 0);

    //The First row for the column names.
    for (int i= 0; i < tab.getGrid().getGridColumnsList().size(); i++)
    {
        TextView tvName = new TextView(getActivity());
        String columnName = tab.getGrid().getGridColumnsList().get(i).getGridColumnAlias();
        tvName.setText(columnName);
        Log.d(TAG, "COLUMN ALIAS FROM FRAGMENT: "+columnName);
        if (i == 0)
        {
            tvName.setLayoutParams(tlparamsFirstColumn);
        }
        else
        {
            tvName.setLayoutParams(tlparams);
        }
        tvName.setGravity(Gravity.CENTER);
        tvName.setTextColor(getResources().getColor(R.color.my_white));  
        tvName.setPadding(10, 10, 10, 10);
        tvName.setBackgroundDrawable(getResources().getDrawable(R.drawable.grid_column_name_background));   
        tableRow.addView(tvName);
    }
    table.addView(tableRow);

    for (int j = 0; j < tab.getGrid().getGridData().getNumRows(); j++)
    {
        TableRow newRow = new TableRow(getActivity());
        TableLayout.LayoutParams insideRowParams=new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT,TableLayout.LayoutParams.WRAP_CONTENT);
        tableRowParams.setMargins(0, 2, 0, 0);
        for (int k = 0; k < tab.getGrid().getGridData().getNumCols(j); k++)
        {
            TextView tvName = new TextView(getActivity());
            String columnName = tab.getGrid().getGridData().get(j, k);
            tvName.setText(columnName);

            if ( (j % 2) == 0)
            {
                tvName.setBackgroundDrawable(getResources().getDrawable(R.drawable.grid_first_row_background)); 
            }
            else
            {
                tvName.setBackgroundDrawable(getResources().getDrawable(R.drawable.grid_second_row_background));    
            }
            if (k == 0)
            {
                tvName.setLayoutParams(tlparamsFirstColumn);
            }
            else
            {
                tvName.setLayoutParams(tlparams);
            }
            tvName.setGravity(Gravity.CENTER);
            tvName.setTextColor(getResources().getColor(R.color.my_black));  
            tvName.setPadding(10, 10, 10, 10);
            newRow.addView(tvName);  
        }
        table.addView(newRow);          
    }
    return fragmetLayout;
}

UPDATE:

My ViewPagerActivity Layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<TabHost
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_below="@+id/imageView3" >

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1" >
        </android.support.v4.view.ViewPager>

         <TabWidget
            android:id="@android:id/tabs"
            android:orientation="horizontal"
            android:layout_gravity="center_horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</TabHost>

 <ImageView
    android:id="@+id/imageView3"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="-3dp"
    android:background="@drawable/login_scr_top_bar"
    android:contentDescription="@drawable/login_scr_top_bar" />

<TextView
    android:id="@+id/tvReportName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="top|left"
    android:paddingLeft="15dp"
    android:paddingTop="13dp"
    android:text="@string/report_tabs"
    android:textColor="@color/my_black"
    android:textSize="18sp"
    android:textStyle="bold" />

<LinearLayout
    android:id="@+id/llTabsButtonsContainer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentRight="true"
    android:gravity="center_horizontal"
    android:orientation="horizontal"
    android:paddingBottom="5dp"
    android:paddingTop="5dp" > 
</LinearLayout>

<TextView
    android:id="@+id/tvReportTitle"
    android:layout_width="wrap_content"
    android:layout_height="20dp"
    android:layout_alignBaseline="@+id/tvReportName"
    android:layout_alignBottom="@+id/tvReportName"
    android:layout_toLeftOf="@+id/bBackToParameters"
    android:layout_toRightOf="@+id/tvReportName"
    android:text="TextView"
    android:textSize="18sp" />

<Button
    android:id="@+id/bBackToParameters"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@+id/tvReportTitle"
    android:layout_marginRight="10dp"
    android:layout_alignParentRight="true"
    android:background="@drawable/button_back_to_parameters_selector"
    android:clickable="true"
    android:onClick="backToParametersButtonOnClick"
    android:padding="3dp"
    android:text="@string/back_to_parameters"
    android:textColor="@color/my_white"
    android:textStyle="bold" />

</RelativeLayout>

and My GridFramgnet Layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<LinearLayout
android:id="@+id/llParametersContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>

<TextView 
    android:id="@+id/tvTabName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    android:textColor="@color/my_black"
    android:textStyle="bold"
    android:textSize="18sp"
    android:layout_marginLeft="10dp"
    android:text="It a test string for report Name"/>

    <ScrollView 
    android:id="@+id/layout" 
    android:layout_height="match_parent"         
    android:scrollbars="horizontal|vertical" 
    android:layout_width="match_parent"     
    android:layout_marginTop="5dip"     
    android:scrollbarStyle="outsideInset"
    android:fillViewport="true"> 

    <HorizontalScrollView 
        android:id="@+id/horizontalView" 
        android:layout_height="wrap_content"     
        android:scrollbars="horizontal|vertical" 
        android:layout_width="wrap_content"     
        android:layout_marginTop="5dip">

        <TableLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/tlGridTable" >   
        </TableLayout>
    </HorizontalScrollView>
   </ScrollView>
 </LinearLayout>

The Problem This for 3 Fragments takes about 10 seconds to load, so the user get a black screen for 10 second before the Activity is actually loaded. how can I avoid it? Running all the process of populating the tables in an AsyncTask will make my ViewPager Activity to load faster? can it even be done?

UPDATE: Most of the time is beeing spend to populate/create the TableLayout. If there is a big amount of data recieved from the server then it's needed to create a big amount of TextViews and set them in the TableLayout.

UPDATE #2: Well I'm over eliminating the problem because as I understand for my needs (horizontal and vertical scrolling of whole view) I have to use TableLayout, So I need to wait this time it takes to load it. But I need to find a way to load the data in the fragments at least with the user knowing that the data is being populated (displaying appropriate Dialog). I tried to put a dialog in the onCreate of the ViewPager before I start the initialization of the fragment but for some reason it not showing and I only see the black screen until the fragments are loaded.

UPDATE:

Please refer to last question as I understand it's not possible to fix the loading times:

The Question: Is there a way to create a ViewPager with almost empty fragments(only a separate headline for each one), present them to the user and then, after the activity is already loaded and visible (and not a black screen is shown), present a Dialog and populate the fragments in while the Dialog is shown?

Any help would be appreciated.

Thanks.

Emil Adz
  • 40,709
  • 36
  • 140
  • 187
  • Android `TableLayouts` are notoriously slow. Is it possible to replace your tables with a listview and a custom adapter? Feel free to post a screenshot of what you are trying to accomplish and I can help ya build a custom adapter for it. – edthethird Mar 21 '13 at 23:18
  • No, unfortunately I think not. I need to load data into tables that can scroll vertically and horizontally(which for what i know can't be achieved with ListViews) – Emil Adz Mar 21 '13 at 23:21
  • Are you measuring this with debugger connected? – TacB0sS Mar 22 '13 at 00:15
  • what do you mean? I measure this on a galaxy s3. didn't checked the output of the stack trace of this moment. what can I see there? the is not crashing it's just takes long time to load an activity – Emil Adz Mar 22 '13 at 07:52
  • Have you tried using [setOffscreenPageLimit](http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int))? I wouldn't use this as a long term solution but, for now, try calling that with zero as a parameter. – crazylpfan Mar 24 '13 at 08:25
  • Yes I did tried it, and it didn't helped me. as I said I'm using 4 fragment max... so the problem is not with the number of fragment that get initialized on start of ViewPager. – Emil Adz Mar 24 '13 at 09:44
  • Use Traceview to determine where your time is being spent. – CommonsWare Mar 25 '13 at 12:35
  • my time is beeing spent on creating a large amount of TextViews in the TableLayouts inside the fragments. When I have less data The load times go down. – Emil Adz Mar 25 '13 at 12:38

3 Answers3

5

This for 3 Fragments takes about 10 seconds to load, so the user get a black screen for 10 second before the Activity is actually loaded.

The reason for that is the amount of widgets you use in your layouts plus the depth of the view hierarchy of your Activity. From what you posted you have something like this:

Possible wrapper for ViewPager in the Activity -> ViewPager -> At least a ScrollView(as you say something about scrolling vertically and horizontally) -> Linearlayout -> TableLayout -> TableRow -> content of TableRow

That is a depth of 7(if I'm not mistaken the support fragment framework also inserts some views in between). This is very bad for performance. You also have a lot of widgets(assuming those for loops run for at most 10 iterations, otherwise it's just really bad) in a single Fragment and taking in consideration that the second fragment will also have its view loaded you can see that this will be again a performance hit.

It would be helpful to post your current layouts to see if they couldn't be optimized.

There are small improvements that you could make to your current code but this will not help you to go from 10 seconds to at most 1 second like you should:

There is no point in this code:

if (container == null) {
    return null;
}

I don't know what this does:

SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository()

but see if you don't create useless objects(Is that a singleton?). Don't put it in the for loop as it will be run every time, use a normal for to avoid the use of the Iterator:

final int count = SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository().size();
for (int i = 0; i < count; i++) {
     final Parameter parameter = SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository().get(i);
     // ... rest of code

You should do the same in the other for loops.

Retrieve those resources(colors and drawables) outside the for loops, in the loop only assign them to the views.

Edit: Your layout is very deep and with too many views so the performance will be poor. The only way to overcome this is to use a widget that recycles its views(I know what you said). Alternatives would be:

  • creating those views on a background thread(I wouldn't do this because it's very risky)
  • you could modify the fragment to simply inflate the layout in the onCreateView and return combined with showing a loading ProgressDialog. After the Fragment becomes visible you would start creating the real view of the Fragment in chunks and finally attach it to the fragment. This would be very bad for the user experience.

And as you use a ViewPager will have additional work to make things go alright between the page switch. Maybe you could rethink your current setup.

user
  • 86,916
  • 18
  • 197
  • 190
  • I have updated the question with the ViewPager and the GridFragment layouts. you are right regarding the number of views in a TableLayout (basically a lot of TextViews often a lot more the 10 iterations). But I don't have any other choice I have to provide the scrolling capability both ways (vertically and horizontally), So I cant use a list view for this screen. TableLayout is the only way I found to show this kind of data. and It's shows it well only the loading is slow. – Emil Adz Mar 24 '13 at 10:36
  • You are right regarding the for condition as well, I will move it outside of the for loop, to see the impact it makes. but as you already said I don't think It will do a great difference. – Emil Adz Mar 24 '13 at 10:42
  • @EmilAdz Then you don't have much choices, see my edited answer. What you're doing is to much(I've seen that you tested on a S3 which is one of the powerful devices out there, on lower spec devices your code will most likely fail) – user Mar 24 '13 at 11:36
  • Thanks @Luksprog for your edited answer, now if you look in my question I asked: "Running all the process of populating the tables in an AsyncTask will make my ViewPager Activity to load faster? can it even be done?" and you said right now that: creating those views on a background thread(I wouldn't do this because it's very risky). why to you see this as a risky action? and I didn't really understood what you meant with the second preposition. The only solution I see right now for my problem is to present a ProgressDialog box in ViewPager for the time that the fragments are generated. – Emil Adz Mar 24 '13 at 12:31
  • @EmilAdz Those view could use handlers which are tied to the tread where they are created which will lead to problems. The second option(at this you are referring right?) your be to inflate the minimum layout(current xml layout) in the fragment class and add the rest at a later time under the protection of a progressdialog/bar. Basically what you said in the last part of your comment. – user Mar 24 '13 at 15:11
  • that it's!!! because I tried right now to initialize the ProgressDialog in the onCreate of the Fragment, but for some reason it was never show and I still waited for about 10 second to see the whole ViewPager to load. I guess you are right... I have to find a way to initialize the tables as lighter as I can (for example only my base layout like you said) and then populate the TableLayout. Only right now I can't understand how it's done. Do you maybe have some code reference on how it's done? Thanks. – Emil Adz Mar 24 '13 at 15:33
4

Most of the time is beeing spend to populate/create the TableLayout. If there is a big amount of data recieved from the server then it's needed to create a big amount of TextViews and set them in the TableLayout

Then do not use a TableLayout. Use a ListView or some other form of AdapterView, so that you only need to "populate/create" enough to fill the visible space, then can populate more as the user scrolls (if the user scrolls at all).

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • unfortunately I can't. I need to load data into tables that can scroll vertically and horizontally(which for what i know can't be achieved with ListViews or any other AdapterView, or am I wrong?) you can get an understanding of what I'm trying to develop looking into this question too: http://stackoverflow.com/questions/15382753/design-a-horizontalscrollview-inside-of-viewpager-or-use-fragments – Emil Adz Mar 25 '13 at 12:56
  • @EmilAdz: "unfortunately I can't" -- you act like you have a choice. If it is taking 10 seconds to create a `TableLayout`, that `TableLayout` must have hundreds of thousands of cells, and you will run out of heap space. – CommonsWare Mar 25 '13 at 13:33
  • I have design lines that I have to follow... And those tell me that the grid should be scroll-able both ways (vertically and horizontally), but this can't be made using an adapter right? Other then that I'm open to any suggestion. If it needed I would prefer to show a dialog for 10 second while it's loading all the fragment in the ViewPager. but for some reason I can't make it work. – Emil Adz Mar 25 '13 at 13:36
  • 1
    @EmilAdz: "but this can't be made using an adapter right?" -- you will have to create your own `AdapterView` for this, or see if somebody else already has. "I would prefer to show a dialog for 10 second" -- talented programmers want to avoid a dialog entirely, giving the user something to work with immediately. – CommonsWare Mar 25 '13 at 13:40
  • Correct me if I wrong, but if I create a custom layout (which for now I have no idea how it's done and due to time constraints a certainly prefer the Dialog option, but if I do that..) it means that each row is going to be horizontally scrollable and not the whole "Horizontally scrollable list view", which is the desired behavior I'm getting right now using the TableLayout, right? – Emil Adz Mar 25 '13 at 14:08
  • @EmilAdz: "it means that each row is going to be horizontally scrollable and not the whole "Horizontally scrollable list view", which is the desired behavior I'm getting right now using the TableLayout, right?" -- I have no idea, as I do not understand this, sorry. – CommonsWare Mar 25 '13 at 14:14
  • hmm, you can try put ListView in HorizontalScrollView, I make something like it with ScrollView+ViewPager. – antslava Mar 26 '13 at 15:30
4

Since you use pager, you load more then just the first fragment when you start the activity. Make the initial fragment view with a progress bar visible and the table hidden and load the data only when you switch to that Fragment.

in the activity use

mPager.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                Fragment f = mAdapter.getFragment(mPager.getCurrentItem());
                f.loadData(); // when you finish loading the data, hide the progress bar and show the table

            }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

To use mAdapter.getFragment(), inherit your adapter from:

    public abstract class SmartFragmentPagerAdapter extends FragmentPagerAdapter{

    private SparseArray<String> mFragmentTags;
    private FragmentManager mFragmentManager;

    public SmartFragmentPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
        mFragmentManager = fragmentManager;
        mFragmentTags = new SparseArray<String>();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }

    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }

    public FragmentManager getFragmentManager(){
        return mFragmentManager;
    }

}
Prokash Sarkar
  • 11,723
  • 1
  • 37
  • 50
Asaf Pinhassi
  • 15,177
  • 12
  • 106
  • 130