我正在寻找一种优雅的方式来显示片段上的层次结构(树)。
我的想法是在不同的片段上显示每个级别。通过单击一片叶子,我将进入层次结构的下一个级别(也很高兴在这里具有漂亮的滑动动画)。最后,我将到达一个有叶子的位置,这应该导致详细视图。
我已经知道树视图的实现(例如http://code.google.com/p/tree-view-list-android/),但是我希望每个级别都位于一个不同的片段上。像Mac文件浏览器一样,在大屏幕(平板电脑)上彼此相邻显示多个级别也很酷。
主要原因是,我想根据请求从服务器加载每个级别的数据(树很大),其次,我希望布局清晰。另一方面,我不想单独实现每个级别片段,因为分支之间的深度可能非常大。
我的数据当前采用以下形式:每个树节点都由一个TreeElement对象表示,该对象具有ID,名称,TreeElementType(节点或叶)和TreeElements列表作为子级。如果必须修改此结构,那将是可行的。
谁能想到实现这一目标的好方法?
最好,
埃里克
最佳答案
我终于找到了一个可行的解决方案:
我的TreeElement类如下所示:
public class TreeElement
{
private int id;
private TreeElementType type;
private String name;
/**
* List that holds a set of all the children tree elements.
*/
private List<TreeElement> children = new ArrayList<TreeElement>();
/**
* List that holds a set of the courses associated to the TreeElement.
*
* @see Course
*/
private List<Course> courses = new ArrayList<Course>();
public TreeElement()
{
this.type = TreeElementType.NODE;
this.children = new ArrayList<TreeElement>();
}
public TreeElement(int id)
{
this();
this.id = id;
}
public TreeElement(int id, String name)
{
this( id );
this.name = name;
}
public TreeElement(int id, TreeElementType type, String name)
{
this( id, name );
this.type = type;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public TreeElementType getType()
{
return type;
}
public void setType(TreeElementType type)
{
this.type = type;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public List<TreeElement> getChildren() {
return children;
}
public void setChildren(List<TreeElement> children) {
this.children = children;
}
public void addChild(TreeElement child) {
this.children.add(child);
}
public void addChildren(List<TreeElement> children) {
this.children.addAll(children);
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
public void addCourse(Course course) {
this.courses.add(course);
}
public void addCourses(List<Course> courses) {
this.courses.addAll(courses);
}
}
因此,这些TreeElement生成一个层次树。每个TreeElement可以额外容纳一些我想在树中显示的课程。
包含所有寻呼机和页面的片段是这个:
public class BrowserSectionFragment extends Fragment {
private BrowserFragmentPagerAdapter mAdapter;
private ViewPager mPager;
private List<Fragment> fragments = new Vector<Fragment>();
public BrowserSectionFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_browser, container, false);
/*
* Add the root TreeElement-fragment, holding the categories of lowest
* level.
*/
fragments.add(new BrowserPageFragment(this,TreeContainer.getTreeElement(TreeContainer.ROOT_ID)));
mAdapter = new BrowserFragmentPagerAdapter(this.getFragmentManager(), fragments);
mPager = (ViewPager) view.findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int arg0) {}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
@Override
public void onPageSelected(int arg0) {
while (arg0 < fragments.size()-1) {
fragments.remove(fragments.size()-1);
}
/*
* notify the adapter of the changes being made
* causes the adapter to check all elements, if they are still in the
* list of fragments
*/
mAdapter.notifyDataSetChanged();
}
});
return view;
}
/**
* Adds a new fragment to the fragment manager. The data is taken from
* the children list of the TreeElement.
*
* @param clickedElement
*/
public void addFragmentFromElement(TreeElement clickedElement) {
fragments.add(new BrowserPageFragment((this),clickedElement));
mPager.setCurrentItem(fragments.size()-1, true);
}
}
现在,我需要一个PagerAdapter,在本例中为FragmentStatePagerAdapter,以组织Fragments:
public class BrowserFragmentPagerAdapter extends FragmentStatePagerAdapter {
/**
* List that holds all the fragments which are currently accessible
* through the Pager.
*/
private List<Fragment> mFragments;
/**
* Constructor that initializes the list of fragments.
*
* @param fm FragmentsManager
* @param fragments
*/
public BrowserFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public int getItemPosition(Object item) {
/*
* See if fragment is still in the list.
* If yes, the position in the list is returned, if not, POSITION_NONE,
* which causes the Pager to delete the related view.
*/
Fragment fragment = (Fragment) item;
int position = mFragments.indexOf(fragment);
if (position >= 0) {
return position;
} else {
return POSITION_NONE;
}
}
}
最后,我实现了一个显示ListView的PageFragment:
public class BrowserPageFragment extends ListFragment {
public static final String ARG_ELEMENT_ID = "element_id";
TreeElement element;
BrowserSectionFragment frag;
Context context;
public BrowserPageFragment() {
super();
}
public BrowserPageFragment( BrowserSectionFragment frag, TreeElement element ) {
this();
this.frag = frag;
this.element = element;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
/* Creating an array adapter to store the list of countries **/
BrowserListAdapter adapter = new BrowserListAdapter(inflater.getContext(),element);
/* Setting the list adapter for the ListFragment */
setListAdapter(adapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Object clickedElement = l.getAdapter().getItem(position);
if (clickedElement instanceof TreeElement) {
frag.addFragmentFromElement((TreeElement) clickedElement);
} else if (clickedElement instanceof Course) {
/*
* Start a new fragment which shows a detail view of the course.
*/
Course course = (Course) clickedElement;
int courseId = course.getId();
CourseContainer.setCourse(courseId, course);
Fragment fragment = new DetailsFragment();
Bundle args = new Bundle();
args.putInt( DetailsFragment.ARG_COURSE_NUMBER, courseId);
fragment.setArguments(args);
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit();
}
}
}
为了使它完整,我的自定义ListAdapter如下所示:
public class BrowserListAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
TreeElement element;
List<Object> elements = new ArrayList<Object>();
public BrowserListAdapter(Context context, TreeElement element) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.element = element;
elements.addAll(element.getChildren());
elements.addAll(element.getCourses());
}
@Override
public int getCount() {
return elements.size();
}
@Override
public Object getItem(int position) {
return elements.get(position);
}
@Override
public long getItemId(int position) {
Object item = elements.get(position);
if (item instanceof TreeElement) {
return ((TreeElement) item).getId();
} else if (item instanceof Course) {
return ((Course) item).getId();
}
return -1;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = (View) mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
bindView(itemView, position);
return itemView;
}
private void bindView(View itemView, int position) {
Object item = getItem(position);
String text = null;
if (item instanceof TreeElement) {
text = ((TreeElement) item).getName();
} else if (item instanceof Course) {
text = ((Course) item).getTitle();
}
itemView.setId((int) getItemId(position));
TextView title = (TextView) itemView;
title.setText(text);
}
}
也许此解决方案可以帮助某人。这里唯一剩下的问题是:
BrowserPageFragment的构造函数有一个带有参数的构造函数,这很不好。
更改方向时,寻呼机跳至第一页。
后退按钮不会转到上一页。