Expandable ListView is very slow in android os
<p>My expandable <code>listview</code> scroll very slow and take a while when I click on the parent category until I see the <code>child</code> view.</p>

<p>Group List Activity:</p>

<pre><code>public class GroupsListActivity extends Activity {

    String loggedUserId = Model.getInstance().getLoggedUserId();

    List&lt;String&gt; groupsList;
    static ExpandableListView expandableListView;
    HashMap&lt;String, List&lt;Group&gt;&gt; groupCategories = new HashMap&lt;String, List&lt;Group&gt;&gt;();
    static ProgressBar spinner;
    static TextView textLoading;
    ImageButton createCategoryButton;
    static Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Set layout for this activity
        setContentView(R.layout.expandable_list);

        // Set actionbar title
        getActionBar().show();
        getActionBar().setTitle(Html.fromHtml("&lt;font color='#fffffff'&gt;Groups&lt;/font&gt;"));

        if (loggedUserId != null)
            Log.d("TAG", "My Groups for user ID: " + loggedUserId);

        // Connect between buttons to layout id
        expandableListView = (ExpandableListView) findViewById(R.id.exp_list);
        spinner = (ProgressBar) findViewById(R.id.spinner);
        createCategoryButton = (ImageButton) findViewById(R.id.createCategory);
        textLoading = (TextView) findViewById(R.id.textLoading);

        // Loading data to expandable group list asynchronously
        AsyncTask&lt;String, String, HashMap&lt;String, List&lt;Group&gt;&gt;&gt; task = new AsyncTask&lt;String, String, HashMap&lt;String, List&lt;Group&gt;&gt;&gt;() {
            @Override
            protected HashMap&lt;String, List&lt;Group&gt;&gt; doInBackground(String... params) {
                return DataProvider.getInfo();
            }

            @Override
            protected void onPostExecute(HashMap&lt;String, List&lt;Group&gt;&gt; listHashMap) {
                super.onPostExecute(listHashMap);

                // Setting adapter and creating group list
                groupCategories = listHashMap;
                groupsList = new ArrayList&lt;String&gt;(groupCategories.keySet());
                adapter = new Adapter(GroupsListActivity.this, groupCategories, groupsList, GroupsListActivity.this);
                expandableListView.setAdapter(adapter);

                // Hide spinner after loading
                spinner.setVisibility(View.GONE);
                textLoading.setVisibility(View.GONE);
            }
        };
        task.execute();

        // Setting listener for group click
        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int parentPosition, int childPosition, long id) {
                // After selecting a group on row - open contacts list for this group
                expandableListView.setEnabled(false);
                openContactListForGroup(groupCategories.get(groupsList.get(parentPosition)).get(childPosition).getGroupID());
                return true;
            }
        });

        // Setting listener for create group click
        createCategoryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createCategoryButton.setEnabled(false);
                onCategoryCreate(GroupsListActivity.this, createCategoryButton);
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_actionbar_groups, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_create:
                onCreate();
                return true;

            case R.id.action_search:
                onSearch();
                return true;

            case R.id.action_favorites:
                onFavorites();
                return true;

            case R.id.action_settings:
                onSettings();
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }

    // Menu methods
    private void onCreate() {
        Log.d("TAG", "Create button was pressed");
        Intent i = new
                Intent(getApplicationContext(),
                CreateGroupActivity.class);

        startActivity(i);
        overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
    }

    private void onSearch() {
        Log.d("TAG", "Search button was pressed");
        Intent i = new
                Intent(getApplicationContext(),
                SearchActivity.class);

        startActivity(i);
        overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
    }

    private void onFavorites() {
        Log.d("TAG", "Favorites button was pressed");
        Intent i = new
                Intent(getApplicationContext(),
                FavoritesListActivity.class);

        startActivity(i);
        overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
    }

    private void onSettings() {
        Log.d("TAG", "Settings button was pressed");

        // Settings activity
        Intent i = new
                Intent(getApplicationContext(),
                SettingsActivity.class);

        startActivity(i);
        overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
    }

    // Methods to handle action buttons
    private void onCategoryCreate(final Activity activity, final ImageButton createCategoryButton) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        createCategoryButton.setEnabled(true);

        final String title = "Create a new category";
        String message = "Type a name for your new category";

        // Set dialog edit_text
        final EditText categoryNameTextView = new EditText(activity);
        categoryNameTextView.setHint("Type your category name");
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT);
        categoryNameTextView.setLayoutParams(lp);
        builder.setView(categoryNameTextView);

        // Set dialog title and message
        if (title != null)
            builder.setTitle(Html.fromHtml("&lt;font color='#dc1c1c'&gt;" + title + "&lt;/font&gt;")).setMessage(message);

        // Set dialog buttons
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                final String newCategoryName = categoryNameTextView.getText().toString();

                // Check if contains only spaces
                if (!(newCategoryName.trim().length() &gt; 0))
                    Toast.makeText(activity, "Type at least 1 letter to create the category", Toast.LENGTH_LONG).show();

                    // Check if category name already exists
                else if (groupsList.contains(newCategoryName))
                    Toast.makeText(activity, newCategoryName + " already exist. Please type another category name", Toast.LENGTH_LONG).show();

                else {
                    // Create a new category in server and add user to a sample group
                    adapter.getCategoriesList().add(newCategoryName);
                    adapter.getGroupsList().put(newCategoryName, Collections.&lt;Group&gt;emptyList());

                    // Update adapter and show toast to user
                    GroupsListActivity.updateAdapter();
                    Toast.makeText(activity, "You created " + newCategoryName + " category", Toast.LENGTH_LONG).show();
                }
            }
        });
        builder.setNegativeButton(
                "Cancel",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });

        builder.setIcon(R.mipmap.edit);
        builder.show();
    }

    public void openContactListForGroup(String groupId) {
        // Contacts List activity
        Intent i = new
                Intent(getApplicationContext(),
                ContactsListActivity.class);

        // Pass to details activity the logged group id and start activity
        Bundle b = new Bundle();
        b.putString("groupId", groupId);
        i.putExtras(b);

        startActivity(i);
        overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
    }

    // Static methods to use from other activities
    public static void updateAdapter() {
        spinner.setVisibility(View.VISIBLE);
        adapter.notifyDataSetChanged();

        // Hide spinner after adapter finish the update
        expandableListView.post(new Runnable() {
            @Override
            public void run() {
                spinner.setVisibility(View.GONE);
            }
        });
    }

    public static void addGroupToList(String groupId) {
        Model.getInstance().getGroup(groupId, new Model.groupReturnedListener() {
            @Override
            public void addGroupToLocal(Group group) {
                // Add group to category Others in Group List Activity
                if (adapter.getGroupsList().get("Others").size() == 0) {
                    // Add group to empty list
                    List&lt;Group&gt; list = new LinkedList&lt;Group&gt;();
                    list.add(group);
                    adapter.getGroupsList().put("Others", list);
                    adapter.notifyDataSetChanged();
                } else {
                    // Add group to an existing list
                    adapter.getGroupsList().get("Others").add(group);
                    adapter.notifyDataSetChanged();
                }
            }
        });
    }

    public static void removeGroupFromList(String groupId) {
        int position = -1;

        // Get category position
        String oldCategoryName = Model.getInstance().getCategoryNameByGroupId(groupId);
        List&lt;Group&gt; data = adapter.getGroupsList().get(oldCategoryName);

        // Search for group position
        for (Group group : data) {
            if (group.getGroupID().equals(groupId)) {
                position = data.indexOf(group);
                break;
            }
        }

        // Groups was found
        if (position != -1) {
            data.remove(position);
            adapter.notifyDataSetChanged();
        }
    }

    public static void updateGroupFromList(Group group) {
        int position = -1;

        // Get category position
        String oldCategoryName = Model.getInstance().getCategoryNameByGroupId(group.getGroupID());
        List&lt;Group&gt; data = adapter.getGroupsList().get(oldCategoryName);

        // Search for group position
        for (Group groupIterator : data) {
            if (groupIterator.getGroupID().equals(group.getGroupID())) {
                position = data.indexOf(groupIterator);
                break;
            }
        }

        // Groups was found
        if (position != -1) {
            data.remove(position);
            data.add(group);
            adapter.notifyDataSetChanged();
        }
    }

    // Other methods
    @Override
    protected void onResume() {
        super.onResume();
        expandableListView.setEnabled(true);
    }

    @Override
    public void onBackPressed() {
        ExitDialog exitDialog = new ExitDialog(GroupsListActivity.this);
        exitDialog.show();
    }
}
</code></pre>

<p>This is the activity of the Adapter where I load the expandable <code>listview</code> :</p>

<pre><code>   @Override
    public View getGroupView(final int parent, boolean isExpanded, View convertView, ViewGroup parentView) {
        final String categoryName = (String)getGroup(parent);

        ParentViewHolder pHolder = null;

        if(convertView == null) {
            pHolder = new ParentViewHolder();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.expandable_list_parent, parentView, false);

            // Connect between buttons to layout id
            pHolder.categoryNameTextView = (TextView) convertView.findViewById(R.id.categoryName);
            pHolder.editCategory = (ImageButton) convertView.findViewById(R.id.editCategory);
            pHolder.deleteCategory = (ImageButton) convertView.findViewById(R.id.deleteCategory);
            convertView.setTag(pHolder);
        }
        else {
            pHolder = (ParentViewHolder) convertView.getTag();
        }

        // Hide edit and delete button for category name Others
        if(categoriesList.get(parent).equals("Others")){
            pHolder.editCategory.setVisibility(View.GONE);
            pHolder.deleteCategory.setVisibility(View.GONE);
        }

        else {
            pHolder.editCategory.setVisibility(View.VISIBLE);
            pHolder.deleteCategory.setVisibility(View.VISIBLE);
        }

        // Set category name on row
        pHolder.categoryNameTextView.setTypeface(null, Typeface.BOLD);
        pHolder.categoryNameTextView.setText(categoryName + ": " + getChildrenCount(parent));

        // Set edit category button listener
        final ParentViewHolder finalPHolder = pHolder;
        pHolder.editCategory.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finalPHolder.editCategory.setEnabled(false);
                editCategoryName(activity, finalPHolder.categoryNameTextView.getText().toString().toString().split(": ")[0], finalPHolder.editCategory, parent);
            }
        });

        // Set delete category button listener
        pHolder.deleteCategory.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finalPHolder.deleteCategory.setEnabled(false);
                deleteCategory(activity, categoryName, finalPHolder.deleteCategory);
            }
        });

        return convertView;
    }

    @Override
    public View getChildView(final int parent, final int child, boolean lastChild, View convertView, ViewGroup parentView) {
        final Group group = (Group)getChild(parent, child);

        ChildViewHolder cHolder = null;

        if(convertView == null){
            cHolder = new ChildViewHolder();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.expandable_list_child, parentView, false);

            // Connect between buttons to layout id
            cHolder.groupImage = (ImageView) convertView.findViewById(R.id.groupImage);
            cHolder.groupName = (TextView) convertView.findViewById(R.id.groupName);
            cHolder.moveCategory = (ImageButton) convertView.findViewById(R.id.moveCategory);
            cHolder.groupFavoritesButton = (ImageButton) convertView.findViewById(R.id.groupFavorites);
            cHolder.groupLeaveGroupButton = (Button) convertView.findViewById(R.id.groupLeave);
            cHolder.groupImageProgressbar = (ProgressBar) convertView.findViewById(R.id.groupImageProgressBar);
            convertView.setTag(cHolder);
        } else {
            cHolder = (ChildViewHolder) convertView.getTag();
        }

        // Set group name on row
        cHolder.groupName.setText(group.getName());

        // Load group image
        cHolder.groupImageProgressbar.setVisibility(View.VISIBLE);
        final ChildViewHolder finalHolder = cHolder;
        Model.getInstance().getGroupImage(group.getImageName(), new Model.LoadImageListener() {
            @Override
            public void onResult(Bitmap imageBmp) {
                finalHolder.groupImage.setImageBitmap(imageBmp);
                finalHolder.groupImageProgressbar.setVisibility(View.GONE);
                finalHolder.groupImage.setVisibility(View.VISIBLE);
            }
        });

        // Set move category button listener
        cHolder.moveCategory.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finalHolder.moveCategory.setEnabled(false);
                showDialogMoveCategory(activity, group.getGroupID(), finalHolder.moveCategory);
            }
        });


        // After click on group image - open profile for this group
        cHolder.groupImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onGroupSelected(group.getGroupID());
            }
        });

        // Setting favorite Button Image
        boolean isFavorite = Model.getInstance().groupIsFavorite(loggedUserId, group.getGroupID());

        if(isFavorite)
            cHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_on);
        else
            cHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_off);

        // Setting favorite Button Action
        cHolder.groupFavoritesButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Add group to favorites
                if (!Model.getInstance().groupIsFavorite(loggedUserId, group.getGroupID())) {
                    finalHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_on);
                    Toast.makeText(activity,
                            "The group " + group.getName() + " was added to favorites", Toast.LENGTH_SHORT).show();
                    Model.getInstance().changeFavoriteStatus(loggedUserId, group.getGroupID(), "true");

                } else {
                    // Delete group from favorites
                    finalHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_off);
                    Toast.makeText(activity,
                            "The group " + group.getName() + " was removed from favorites", Toast.LENGTH_SHORT).show();
                    Model.getInstance().changeFavoriteStatus(loggedUserId, group.getGroupID(), "false");
                }
            }
        });

        // After click on group action - leave group
        cHolder.groupLeaveGroupButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finalHolder.groupLeaveGroupButton.setEnabled(false);
                showDialogLeaveGroup(activity, "Are you sure ?", "This action will remove yourself from the group " + group.getName(), group.getGroupID(), parent, child);
                finalHolder.groupLeaveGroupButton.setEnabled(true);
            }
        });

        return convertView;
    }
</code></pre>

<p>The loading image method:</p>

<pre><code>public void getGroupImage(final String imageName, final LoadImageListener listener) {
        AsyncTask&lt;String, String, Bitmap&gt; task = new AsyncTask&lt;String, String, Bitmap&gt;() {
            @Override
            protected Bitmap doInBackground(String... params) {
                Bitmap bmp = loadImageFromFile(imageName);              //first try to find the image on the device
                //  Bitmap bmp = null;
                if (bmp == null) {                                      //if image not found - try downloading it from parse
                    bmp = modelParse.getGroupImage(imageName);
                    if (bmp != null)
                        saveImageToFile(bmp, imageName);    //save the image locally for next time *****
                }
                Bitmap scaledBitmap = scaleDown(bmp, 200, true);
                return scaledBitmap;
            }

            @Override
            protected void onPostExecute(Bitmap result) {
                listener.onResult(result);
            }
        };
        task.execute();
    }

 private void saveImageToFile(Bitmap imageBitmap, String imageFileName) {
        FileOutputStream fos;
        OutputStream out = null;
        try {
            File dir = context.getExternalFilesDir(null);
            out = new FileOutputStream(new File(dir, imageFileName + ".jpg"));
            imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private Bitmap loadImageFromFile(String fileName) {
        Bitmap bitmap = null;
        try {
            File dir = context.getExternalFilesDir(null);
            InputStream inputStream = new FileInputStream(new File(dir, fileName + ".jpg"));
            bitmap = BitmapFactory.decodeStream(inputStream);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
</code></pre>