vga*_*nin 8 android android-custom-view android-layout android-adapterview
我知道我应该测量孩子onMeasure()
并将其布局onLayout()
.问题是我应该在这些方法中添加/回收视图,以便我可以一起测量所有孩子,看看它们是如何相互定位的(即网格,列表或其他)?
我的第一种方法是在添加/回收的观点onLayout()
,但是从这一点来说,因为他们不会被添加到适配器视图但我不能衡量我的孩子们getChildCount()
回报0
的onMeasure()
.如果没有孩子已经布置,我无法测量AdapterView本身,因为它真的取决于他们的相互位置,对吗?
当动态添加/删除子项时,我对AdapterView中的android布局过程感到困惑.
我无法发表评论,因为我是新用户,但是你能描述一下你想要做什么,而不是你想要如何做吗?通常,您会发现这是一个设计问题,而不是编码问题。特别是如果您来自不同的平台(例如 iOS)。根据经验,我发现如果您根据业务需求正确设计布局,那么在 Android 中测量和手动布局几乎是不必要的。
编辑:正如我提到的,这可以使用一些设计决策来解决。我将使用您的节点/列表示例(希望这是您的实际用例,但可以扩展解决方案以解决更普遍的问题)。
因此,如果我们将您的标题视为论坛中的评论,并将列表视为对您评论的回复,我们可以做出以下假设:
一张清单就足够了,不需要两张。列表中的每个项目可以是标题(评论)或列表项目(回复)。每个回复都是评论,但并非所有评论都是回复。
对于项目 n,我知道它是评论还是回复(即它是标题还是列表中的项目)。
现在,您可以使用以下组件:
那么让我们看一些代码,好吗?
首先,您的 XML 文件:
评论行(您的标题)
<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>
Run Code Online (Sandbox Code Playgroud)
现在您的回复行(列表中的一个元素)
<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>
Run Code Online (Sandbox Code Playgroud)
好的,现在你的适配器类
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
}
}
Run Code Online (Sandbox Code Playgroud)
好吧,太好了,现在我们有了一个包含隐藏子项的标头列表(请记住,我们将子项的默认可见性设置为“消失”)。这不是我们想要的,所以让我们解决这个问题。
您的容器类(片段或活动)将具有以下 XML 定义
<!-- 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" />
Run Code Online (Sandbox Code Playgroud)
并且您的 onCreateView 将实现 OnItemClickListener 并具有以下内容
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
}
Run Code Online (Sandbox Code Playgroud)
现在,当您单击标题时显示您的列表
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id)
{
adapter.changeVisibility(position);
}
Run Code Online (Sandbox Code Playgroud)
现在,如果单击一个项目并且该项目有一个父项(即列表项),它将根据其当前状态显示/隐藏。
关于代码的一些评论:
我在写字板上写了这篇文章,因为我手边没有开发环境。对于任何编译错误,我们深表歉意。
该代码可以优化:如果您有一个非常大的数据集,则该代码会很慢,因为您在每次调用changeVisibility()时都会重新绘制整个列表。您可以维护两个列表(一个用于标题,一个用于列表项),并且在changeVisibility中您只能查询列表项)。
我再次强调这样的想法:一些设计决策会让你的生活变得更加轻松。例如,如果您的列表项实际上只是一个标签列表,那么您可以拥有一个自定义 XML 文件(用于标题)和其中的一个 ListView 视图,您可以将其设置为 View.GONE。这将使所有其他视图假装它不存在,并且您的布局将正常工作。
希望这可以帮助。
归档时间: |
|
查看次数: |
996 次 |
最近记录: |