2016-06-08 57 views
2

我可擴展listview滾動非常緩慢,需要一段時間,直到我看到child視圖時單擊父類別。可擴展列表視圖在Android操作系統中非常緩慢

組列表的活動:

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

回答

0

我看到表現的兩個主要問題。

首先,你使用findViewById無處不在。你正在走這整個視圖樹。這就是人們使用ViewHolder模式或自定義視圖模式的原因。如果你在應用程序的生命週期中每次使用findViewById多次,那麼你就錯了。

其次,每次用戶滾動時都會分配新對象。別。使用ViewHolder或vustom視圖模式,以便您可以爲所有滾動事件重複使用相同的OnClickListener,每行僅創建一次,並根據需要更新值。如果您在getView中創建了一個對象,除了convertView爲null時,您的效率會非常低。

+0

剛纔我對此有所瞭解,您能否提供一個高效的教程?如果我理解正確,我應該在第一次加載時初始化按鈕(findViewById),然後使用在滾動之前得到的「舊」信息。 –

+0

這就是基礎知識。與任何Listener類相同 - 在第一次加載時創建它們並在滾動時重用它們(根據需要更改任何內部狀態)。 –

+0

看看我的問題,我添加到getView方法的viewHolder,但仍然緩慢 –

0

我會嘗試給出關於列表視圖和適配器的一般(抽象)概念,它應該可以幫助您自己找出錯誤的部分。

適配器的整個目的是爲相應的列表項目顯示正確的數據,並儘可能做其他儘可能少的工作。在這個過程中,與數據操作相關的任何事情都需要cpu週期,這會導致滯後和慢速滾動。

具體來說,Android應用程序應該以平滑的每秒60幀的速度運行,並且爲了達到60FPS的速率,每幀應該不超過16.6毫秒來渲染。因此,如果您爲CPU創建額外的負載,它可能會出現幀渲染的情況,並且從此處延遲渲染的路徑很短。

我在說什麼 - 您的適配器中可能存在一些方法,它們會同步操作數據,並且會對cpu徵稅。適配器應該代表已準備好顯示的數據,並將其顯示在正確的視圖中。性能問題的一個例子可能就像每次使用String.replace()方法一樣容易,或者另一個壞例子會以同步方式加載圖像,而不是異步加載圖像。

+0

Now ,我使用了所有從模型中同步接收數據的方法。我剛剛開始,並計劃在將來以異步方式制定所有方法。你是說在我改變這些方法後,我會得到一個平滑的滾動?因爲如果我在適配器中初始化1000個對象的列表,而沒有從模型和服務器獲取數據它看起來不錯! 如果你的答案是肯定的,你能否告訴我是否還有其他問題?因爲我使用了@Gabe Sechan告訴我的viewHolder,但我不知道我是否做得正確。請你能告訴我如何使用viewHolder加載圖像嗎?順便說一句我來自ashdod :) –

+0

艾哈很高興在這裏見面:)。是的,這就是我所說的 - 網絡調用應該絕對不會發生在適配器中,它應該在發生之前發生,並且在將模型傳遞給適配器之前,下載的任何東西都應該放在模型中。其次 - 不是所有的方法都應該是異步的,但重載的方法應該是 – Yosi199

+0

你可以看看嗎?我添加了活動代碼和加載圖像方法,我用DataProvider異步加載數據。然後我啓動我的適配器。現在,我可以在應用上看到一個微調器,直到適配器獲取信息,然後更新視圖(它比以前更流暢,直到數據顯示之前,我在黑屏之前)。但是當我滾動或者我嘗試擴展一個類別時,我沒有流暢的列表。你能幫助並告訴我要改變什麼嗎?也許是圖像或其他東西... –