1

フラグメントに階層 (ツリー) を表示するエレガントな方法を探しています。

私の考えは、各レベルを個別のフラグメントに表示することです。葉をクリックすると、階層の次のレベルに移動します (ここでもかなりスライドするアニメーションがあるとよいでしょう)。最後に、詳細ビューにつながるリーフがあるポイントに到達します。

ツリー ビューの実装 ( http://code.google.com/p/tree-view-list-android/など) は既に知っていますが、各レベルを個別のフラグメントに配置したいと考えています。Mac のファイル ブラウザのように、大きな画面 (タブレット) で複数のレベルを並べて表示するのもクールです。

主な理由は、リクエストに応じてサーバーから各レベルのデータをロードしたい (ツリーが非常に大きい) ことと、次に明確なレイアウトが必要であることです。一方で、すべてのレベル フラグメントを個別に実装したくはありません。深さはブランチごとに異なる可能性があるためです。

私のデータは現在、すべてのツリーノードが、ID、名前、TreeElementType (ノードまたはリーフ)、および TreeElements のリストを子として持つ TreeElement オブジェクトによって表されるという形式になっています。この構造を変更する必要がある場合は、実行可能です。

これを実現する良い方法を思いつく人はいますか?

ベスト、エリック

4

1 に答える 1

1

私はついに実用的な解決策を見つけました:

私の 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) が必要です。

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 のコンストラクターには引数付きのコンストラクターがありますが、これは良くありません。
  • 向きを変えると、Pager は最初のページにジャンプします。
  • 戻るボタンは前のページに移動しません。
于 2013-02-24T17:40:42.253 に答える