可展开的ListView在android os中非常慢

时间:2016-06-08 23:58:00

标签: android android-studio scrollbar expandablelistview

我的可扩展listview滚动非常慢,当我点击父类别时我需要一段时间,直到看到child视图。

组列表活动:

public class GroupsListActivity extends Activity {

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

    List<String> groupsList;
    static ExpandableListView expandableListView;
    HashMap<String, List<Group>> groupCategories = new HashMap<String, List<Group>>();
    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("<font color='#fffffff'>Groups</font>"));

        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<String, String, HashMap<String, List<Group>>> task = new AsyncTask<String, String, HashMap<String, List<Group>>>() {
            @Override
            protected HashMap<String, List<Group>> doInBackground(String... params) {
                return DataProvider.getInfo();
            }

            @Override
            protected void onPostExecute(HashMap<String, List<Group>> listHashMap) {
                super.onPostExecute(listHashMap);

                // Setting adapter and creating group list
                groupCategories = listHashMap;
                groupsList = new ArrayList<String>(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("<font color='#dc1c1c'>" + title + "</font>")).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() > 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.<Group>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<Group> list = new LinkedList<Group>();
                    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<Group> 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<Group> 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();
    }
}

这是适配器的活动,我在其中加载了可展开的listview

   @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;
    }

加载图片方法:

public void getGroupImage(final String imageName, final LoadImageListener listener) {
        AsyncTask<String, String, Bitmap> task = new AsyncTask<String, String, Bitmap>() {
            @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;
    }

2 个答案:

答案 0 :(得分:0)

我看到两个主要的性能问题。

首先,你到处使用findViewById。你走的是整个视图树。这就是人们使用ViewHolder模式或自定义视图模式的原因。如果您在应用的生命周期中每次使用findViewById多次,那么您做错了。

其次,每次用户滚动时都会分配新对象。别。使用ViewHolder或vustom视图模式,以便您可以为所有滚动事件重用相同的OnClickListener,每行只创建一次并根据需要更新值。如果你曾经在你的getView中创建一个对象,除了当convertView为null时,你的效率非常低。

答案 1 :(得分:0)

我将尝试给出关于列表视图和适配器的一般(抽象)概念,这可以帮助您自己找出错误的部分。

适配器的整个目的是显示相应列表项的正确数据,并尽可能少地完成其他工作。在这个过程中,任何与数据操作相关的东西都需要cpu循环,这会导致滞后和慢滚动。

具体来说,Android应用程序应该以平滑的每秒60帧的速度运行,并且为了达到60FPS的速率,每帧应该不再需要16.6毫秒来渲染。因此,如果您为CPU创建额外的负载,可能会出现帧渲染的问题,从这里开始的渲染滞后路径很短。

我在说什么 - 你的适配器中可能有一些方法是在现场操作数据,同步并且它会对cpu造成负担。适配器应该代表ALREADY准备显示的数据,并在正确的视图中显示它。性能问题的一个示例可能就像每次为每个视图使用String.replace()方法一样简单,或者另一个坏示例将同步而不是异步加载图像。