我正在寻找一种优雅的方式来显示片段上的层次结构(树)。

我的想法是在不同的片段上显示每个级别。通过单击一片叶子,我将进入层次结构的下一个级别(也很高兴在这里具有漂亮的滑动动画)。最后,我将到达一个有叶子的位置,这应该导致详细视图。

我已经知道树视图的实现(例如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的构造函数有一个带有参数的构造函数,这很不好。
更改方向时,寻呼机跳至第一页。
后退按钮不会转到上一页。

10-07 19:16