8

Çocukları onMeasure() numaralı telefondan ölçmem ve bunları onLayout() içinde düzenlemem gerektiğini biliyorum. Soru, bu yöntemleri ne şekilde eklemem gerektiğini/geri dönüşümü tekrarlamalıyım, böylece tüm çocukları birlikte nasıl konumlandıkları konusunda bir gözle ölçebildim (örneğin, ızgara, liste ya da her neyse)?Özel Adaptörde onMeasure() ve onLayout() yöntemlerini uygulamak için doğru yol

İlk yaklaşım onLayout() görünümleri geri dönüşüm ancak onMeasure() yılında 0 henüz AdapterView eklenmiş ve getChildCount() döner olmadığından bu noktadan ben çocuklarıma ölçemez/eklemek oldu. AdaptViewView'un kendisini zaten düzenlenmiş çocuklar olmadan ölçemiyorum çünkü gerçekten onların karşılıklı pozisyonlarına bağlı, değil mi?

Çocuklar dinamik olarak eklendiğinde/çıkarıldığında AdapterView uygulamasında android düzeni işlemi ile gerçekten kafam karıştı.

+0

Buna nasıl yaklaşacağınız konusunda hiçbir fikrim yok. Şahsen, ben RecyclerView' kapalı çalışırdı. Orada, çocukları dışarıda bırakma çalışması, özel, takılabilir bir yönetici sınıfı tarafından ele alınmaktadır. Daha net bir API var ve problemi nasıl ele alacağınızı görmek için zaten kullanabileceğiniz üçüncü parti yöneticileri var ('recyclerview-v7' içinde üç tane nakliye ile birlikte). – CommonsWare

cevap

1

Yeni bir kullanıcı olduğum için yorum yapamıyorum, ancak yapmaya çalıştığınız NASIL'ın tersine, NE yapmaya çalıştığınızı açıklayabilir misiniz? Sıklıkla, bunun kodlamanın aksine bir tasarım sorunu olduğunu göreceksiniz. Özellikle farklı bir platformdan geliyorsanız (örnek, iOS). Tecrübenizden, iş gereksinimlerinizin ışığında düzeninizi düzgün bir şekilde tasarlamanız durumunda, Android'deki ölçüm ve manuel düzenlerin çoğunlukla gereksiz olduğunu buldum.

DÜZENLEME: Daha önce de belirttiğim gibi bu, bazı tasarım kararları kullanılarak çözülebilir. Düğümler/Liste örneğinizi kullanacağım (bunun gerçek kullanım durumunuz olduğunu umuyorum, ancak çözüm daha genel bir sorun için genişletilebilir).

  1. Bir liste yeterli değil ikidir:

    Bu yüzden bir forumda bir açıklama ve yorumunuza yanıt olarak Listesi'nde olarak Üstbilgi düşünecek olursak, aşağıda varsayımı yapabilirsiniz. Listedeki her öğe bir başlık (yorum) veya bir liste öğesi (cevap) olabilir. Her cevap bir yorumdur, ancak tüm yorumlar cevap değildir.

  2. Öğe n'de bunun bir açıklama veya yanıt olup olmadığını (ör. Listenizdeki bir başlık veya öğe) biliyorum.

  3. Öğe n'de boolean üyeliğim var Vazgeçilmez (varsayılan yanlış; Görünüm.GONE).

Şimdi, aşağıdaki bileşenleri kullanabilirsiniz:

  1. biri genişletilmiş adaptör sınıfı
  2. İki Düzen XMLs: Cevabınız için Yorum için One, bir. Sınırsız yorumunuz olabilir ve her yorumun sınırsız yanıtı olabilir. Her ikisi de ihtiyaçlarınızı karşılar.
  3. Listenizi göstermek/gizlemek için OnItemClickListener'ı uygulayan parça veya etkinlik kapsayıcı sınıfınız.

Hadi bir kod bakalım mı?

İlk olarak, XML dosyaları:

Yorum satır (sizin başlık)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/overall" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:animateLayoutChanges="true"> 

<TextView 
    android:id="@+id/comment_row_label" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"/> 
</RelativeLayout> 

Şimdi cevap sırası (listenizde unsuru)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/overall" 
android:layout_width="match_parent" 
android:layout_height="wrap_content"> <!-- this is important --> 
<TextView 
    android:id="@+id/reply_row_label" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:visibility="gone"/> <!-- important --> 
</RelativeLayout> 

Tamam, hemen şimdi adaptör sınıf

public class CommentsListAdapter extends BaseAdapter implements OnClickListener 
{ 

public static String TAG = "CommentsListAdapter"; 

private final int NORMAL_COMMENT_TYPE = 0; 
private final int REPLY_COMMENT_TYPE = 1; 

private Context context = null; 
private List<Comment> commentEntries = null; 
private LayoutInflater inflater = null; 

//All replies are comments, but not all comments are replies. The commentsList includes all your data. (Remember that the refresh method allows you to add items to the list at runtime. 
public CommentsListAdapter(Context context, List<Comment> commentsList) 
{ 
    super(); 

    this.context = context; 
    this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    this.commentEntries = commentsList; 

} 
//For our first XML layout file 
public static class CommentViewHolder 
{ 
    public RelativeLayout overall; 
    public TextView label; 
} 

//For our second XML 
public static class ReplyViewHolder 
{ 
    public RelativeView replyOverall; 
    public TextView replyLabel; 
} 

@Override 
public int getViewTypeCount() 
{ 
    return 2; //Important. We have two views, Comment and reply. 
} 

//Change the following method to determine if the current item is a header or a list item. 
@Override 
public int getItemViewType(int position) 
{ 
    int type = -1; 
    if(commentEntries.get(position).getParentKey() == null) 
     type = NORMAL_COMMENT_TYPE; 
    else if(commentEntries.get(position).getParentKey() == 0L) 
     type = NORMAL_COMMENT_TYPE; 
    else 
     type = REPLY_COMMENT_TYPE; 

    return type; 
} 

@Override 
public int getCount() 
{ 
    return this.commentEntries.size(); //all data 
} 

@Override 
public Object getItem(int position) 
{ 
    return this.commentEntries.get(position); 
} 

@Override 
public long getItemId(int position) 
{ 
    return this.commentEntries.indexOf(this.commentEntries.get(position)); 
} 

@Override 
public View getView(int position, View convertView, ViewGroup parent) 
{ 
    CommentViewHolder holder = null; 
    ReplyViewHolder replyHolder = null; 

    int type = getItemViewType(position); 

    if(convertView == null) 
    { 
     if(type == NORMAL_COMMENT_TYPE) 
     { 
      convertView = inflater.inflate(R.layout.row_comment_entry, null); 
      holder = new CommentViewHolder(); 
      holder.label =(TextView)convertView.findViewById(R.id.comment_row_label); 
      convertView.setTag(holder); 
     } 
     else if(type == REPLY_COMMENT_TYPE) 
     { 
      convertView = inflater.inflate(R.layout.row_comment_reply_entry, null); 
      replyHolder = new ReplyViewHolder(); 
      replyHolder.replyLable = (TextView)convertView.findViewById(R.id.reply_row_label); 
      convertView.setTag(replyHolder); 
     } 
    } 
    else 
    { 
     if(type == NORMAL_COMMENT_TYPE) 
     { 
      holder = (CommentViewHolder)convertView.getTag(); 
     } 
     else if(type == REPLY_COMMENT_TYPE) 
     { 
      replyHolder = (ReplyViewHolder)convertView.getTag(); 
     } 
    } 
    //Now, set the values of your labels 
    if(type == NORMAL_COMMENT_TYPE) 
    { 
     holder.label.setTag((Integer)position); //Important for onClick handling 

     //your data model object 
     Comment entry = (Comment)getItem(position); 
     holder.label.setText(entry.getLabel()); 
    } 
    else if(type == REPLY_COMMENT_TYPE) 
    { 
     replyHolder = (ReplyViewHolder)convertView.getTag(); //if you want to implement onClick for list items. 

     //Or another data model if you decide to use multiple Lists 
     Comment entry = (Comment)getItem(position); 
     replyHolder.replyLabel.setText(entry.getLabel())); 

     //This is the key 
     if(entry.getVisible() == true) 
      replyHolder.replyLabel.setVisibility(View.VISIBLE); 
     else 
      replyHolder.replyLabel.setVisibility(View.GONE); 
    } 

    return convertView; 

} 

//You can use this method to add items to your list. Remember that if you are using two data models, then you will have to send the correct model list here and create another refresh method for the other list. 
public void refresh(List<Comment> commentsList) 
{ 
    try 
    { 
     this.commentEntries = commentsList; 
     notifyDataSetChanged(); 
    } 
    catch(Exception e) 
    { 
     e.printStackTrace(); 
     Log.d(TAG, "::Error refreshing comments list.");   
    } 
} 

//Utility method to show/hide your list items 
public void changeVisibility(int position) 
{ 
    if(this.commentEntries == null || this.commentEntries.size() == 0) 
     return; 
    Comment parent = (Comment)getItem(position); 
    for(Comment entry : this.commentEntries) 
    { 
     if(entry.getParent().isEqual(parent)) 
      entry.setVisible(!entry.getVisible()); //if it's shown, hide it. Show it otherwise. 
    } 
    notifyDataSetChanged(); //redraw 
} 

} 

Tamam harika, şimdi gizli çocuklara sahip başlıkların bir listesini görüyoruz (unutmayın, çocukların 'görünmesine' varsayılan görünürlüklerini ayarladık). İstediklerimizi değil, hadi bunu düzeltelim.

Kapsayıcınız sınıfı (fragman veya aktivite) Eğer

<!-- the @null divider means transparent --> 
<ListView 
    android:id="@+id/comments_entries_list" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:divider="@null" 
    android:dividerHeight="5dp" /> 

aşağıdaki XML tanım olacaktır Ve onCreateView Şimdi

private ListView commentsListView = null; 
private List<Comment>comments = null; 
private static CommentsListAdapter adapter = null; 
.... 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{ 
... 
//comments list can be null here, and you can use adapter.refresh(data) to set the data 
adapter = new CommentsListAdapter(getActivity(), comments); 
this.commentsListView.setAdapter(adapter); 
this.commentsListView.setOnClickListener(this); //to show your list 
} 

listenizi göstermek için aşağıdaki OnItemClickListener uygulamak ve olacak zaman sizi Bir öğe c varsa, şimdi bir başlık tıklatın. yaladı ve bu öğe bir ebeveyne sahip (örn. liste öğesi), mevcut durumuna göre gösterilecek/gizlenecektir. kodu hakkında

Bazı yorumlar: Ben kullanışlı bir dev ortamı yok gibi

  1. Ben WordPad bu yazdım. Derleme hataları için özür dilerim.

  2. Bu kod en iyi duruma getirilebilir: Çok büyük bir veri kümeniz varsa, bu kod, değişikliklerin tamamınınVisibility() öğesinin her çağrısında yeniden çizildiğinden yavaş olur. İki listeyi (başlıklar için bir, liste öğeleri için bir tane) ve yalnızca liste öğeleri üzerinde sorgulayabileceğiniz changeVisibility içinde tutabilirsiniz.

  3. Bu tasarım fikrini, bazı tasarım kararlarının hayatınızı çok daha kolaylaştıracağını düşünüyorum. Örneğin, liste öğeleriniz aslında bir etiket listesiyse, özel bir XML dosyanız (başlığınız için) ve bunun içinde View.GONE olarak ayarlayabileceğiniz bir ListView görünümü olabilir. Bu, diğer tüm görüşleri, orada bile olmadığını ve düzeninizin düzgün çalışacağını iddia eder.

Bu yardımcı olur umarım.

+0

Yanıt için teşekkürler. Amacım, 'wrap_content' özel AdapterView'im için çalışmak, yani adaptör tarafından verilen elemanların sayısının az olması durumunda AdapterView, tüm kullanılabilir alanı almamaya çalışır. – vganin

+0

Belirli bir kullanım durumu: düğümlerin bileşik görünümleri (her zaman görünür) ve listeden (üstbilgi tıklama olayında genişler) oluşan düğüm görünümleri olan iki düzeyli menü. List benim özel Adaptörüm. Liste herhangi bir sayıda öğe içerebilir. Sorun şu ki, liste her zaman tüm yüksekliği elde etmeye çalışır. – vganin

+0

DÜZENLEME: Bana yardım ettiğiniz tüm çabalarınız için teşekkür ederim. Aslında ben zaten senin yaptığın yolu düşündüm ve tam olarak bu şekilde yapmamı engelleyen engeller var. Görüyorsunuz, benim özel Adaptörümün ana özelliği, 'Drawable' seçicisini canlandırması ve bu seçicinin nodları düzgün bir şekilde yanıtlamak için yorum düğümlerinden hareket etmesine ihtiyacım var VE yanıt düğümleri gruplarda organize edilmeli ve böylece genişletmek için başka bir animasyon kullanabilmem gerekir. Onları birlikte çöker. Ancak bu hedefi çok karmaşık bir şekilde başarmayı başardım. – vganin