Tôi có thể sử dụng máy nhắn tin xem với lượt xem (không phải với các đoạn)


131

Tôi đang sử dụng ViewPagerđể vuốt giữa Fragments, nhưng tôi có thể sử dụng ViewPagerđể vuốt giữa Viewsbố cục XML đơn giản không?

Đây là trang của tôi Adaptercho ViewPager được sử dụng để vuốt giữa các mảnh:

import java.util.List;

import com.app.name.fragments.TipsFragment;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.view.ViewGroup;

public class PageAdapter extends FragmentPagerAdapter {

    /**
     *
     */
    List<Fragment> fragments;
    public PageAdapter(FragmentManager fm,List<Fragment> frags) {
        super(fm);
        fragments = frags;

    }

    @Override
    public Fragment getItem(int arg0) {
        // TODO Auto-generated method stub
        return TipsFragment.newInstance(0, 0);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return 4;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        FragmentManager manager = ((Fragment) object).getFragmentManager();
        FragmentTransaction trans = manager.beginTransaction();
        trans.remove((Fragment) object);
        trans.commit();

        super.destroyItem(container, position, object);
    }

}

Và đây là mảnh vỡ của tôi:

public class TipsFragment extends Fragment
{
    public static TipsFragment newInstance(int image,int content)
    {
        TipsFragment fragment = new TipsFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.tip_layout, null);
        return view;
    }
}

Làm cách nào tôi có thể sửa đổi mã của mình để hoạt động với Lượt xem thay vì Đoạn?


cũng có một cái nhìn về ví dụ này stackoverflow.com/a/37916222/3496570
Zar E Ahmer

Tại sao không sử dụng các mảnh vỡ? Chúng ta sẽ đạt được hoặc mất gì nếu chúng ta sử dụng hoặc không sử dụng các mảnh vỡ?
Eftekhari

@Eftekhari Mảnh vỡ => Vòng đời phức tạp => Lỗi nhiều hơn => Hỗn loạn
Harshil Pansare

1
@HarshilPansare Vâng, tôi đã trải qua tất cả những thảm họa này sau khi tôi hỏi câu hỏi này vào tháng Hai và tôi sẽ không sử dụng các đoạn trong các dự án của mình nữa. Tôi không có lựa chọn nào khác để làm sạch tất cả các mảnh vỡ từ ViewPagertrên onDestroydo đó trên onResumehoạt động sẽ không có cần phải lấy tất cả 3 mảnh vỡ không còn có thể có sẵn. Chỉ muốn đề cập đến một trong những vấn đề.
Eftekhari

Chúc mừng cuộc sống không có mảnh vỡ!
Harshil Pansare

Câu trả lời:


95

Bạn cần ghi đè hai phương thức này thay vì getItem():

@Override
public Object instantiateItem(ViewGroup collection, int position) {
    View v = layoutInflater.inflate(...);
    ...
    collection.addView(v,0);
    return v;
}

@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
    collection.removeView((View) view);
}

5
tốt đẹp .. giúp tôi rất nhiều ... tôi đã mở rộng PageAd CHƯƠNG thay vì FragmentPageAd CHƯƠNG ........ bây giờ nó hoạt động tốt .....
ranjith

3
Để có một ví dụ hoạt động hoàn chỉnh, hãy xem mã được tìm thấy trong câu hỏi này: stackoverflow.com/q/7263291
Tiago

7
Vì tò mò, tại sao addView lại được yêu cầu. Bộ điều hợp mảnh chỉ yêu cầu nó trả về xem.
Amit Gupta

2
bạn không cần phải cast để ViewPagerở tất cả khi bạn đang đối phó với các ViewGroupgiao diện
Dori

2
@AmitGupta bạn được yêu cầu thêm chế độ xem vào vùng chứa vì bạn có thể không quay lại chế độ xem tại đây. InstantiateItem phải trả về một đối tượng được liên kết với khung nhìn đó, có thể là một đối tượng khác nếu bạn muốn; đó là một chìa khóa. Bất cứ điều gì bạn trả lại, bạn chỉ cần đảm bảo rằng việc triển khai isViewFromObject của bạn có thể khớp với hai khóa, để xem. Tuy nhiên, cách thực hiện phổ biến nhất là chỉ trả về khung nhìn là khóa. FragmentPageAdOG xử lý tất cả các công cụ key-to-view cho bạn và do đó chỉ yêu cầu bạn tạo một đoạn thay thế.
themightyjon

66

Sử dụng ví dụ này

Bạn có thể sử dụng một bố cục XML đơn lồng nhau các khung nhìn con.

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/page_one"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
                        <TextView
                        android:text="PAGE ONE IN"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="#fff"
                        android:textSize="24dp"/>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/page_two"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
                        <TextView
                        android:text="PAGE TWO IN"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="#fff"
                        android:textSize="24dp"/>
            </LinearLayout>

    </android.support.v4.view.ViewPager>
</LinearLayout>

NHƯNG ... bạn cũng cần xử lý việc này với một bộ chuyển đổi. Ở đây chúng tôi trả về ID xem được tìm thấy mà không làm tăng bất kỳ bố cục nào khác.

class WizardPagerAdapter extends PagerAdapter {

    public Object instantiateItem(ViewGroup collection, int position) {

        int resId = 0;
        switch (position) {
        case 0:
            resId = R.id.page_one;
            break;
        case 1:
            resId = R.id.page_two;
            break;
        }
        return findViewById(resId);
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == arg1;
    }

    @Override public void destroyItem(ViewGroup container, int position, Object object) {
        // No super
    }
}

// Đặt bộ điều hợp ViewPager

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);

2
@ user1672337 findViewById có thể truy cập từ lớp Activity. Vì vậy, tạo lớp bên trong (không tĩnh) trong Actvitiy của bạn. Hoặc bạn phải truyền ví dụ hoạt động cho bộ điều hợp
ruX 21/12/13

7
Điều này dường như không hoạt động nếu ViewPager có nhiều hơn 2 lượt xem con. Ví dụ: nếu tôi có ba RelativeLayouts trong ViewPager của mình và sau đó thử vuốt sang trang thứ ba, trang thứ ba hiển thị dưới dạng trống. Bất cứ ý tưởng những gì cho?
Nathan Walters

15
@NathanWalters, tôi đã gặp vấn đề tương tự ngày hôm nay, đã giải quyết vấn đề này làm tăng thuộc tính OffscreenPageLimit của ViewPager. Có lẽ nó đáng để cập nhật một câu trả lời với thông tin này.
Mikhail

2
bạn có thể sử dụng return collection.findViewById(resId);Nếu bạn không muốn vượt qua một trường hợp hoạt động
Aksiom

3
Vui lòng thêm mã này@Override public void destroyItem(ViewGroup container, int position, Object object) {}
Volodymyr Kulyk

11

Chúng tôi đã xây dựng một lớp con rất đơn giản ViewPagermà đôi khi chúng tôi sử dụng.

/**
 * View pager used for a finite, low number of pages, where there is no need for
 * optimization.
 */
public class StaticViewPager extends ViewPager {

    /**
     * Initialize the view.
     *
     * @param context
     *            The application context.
     */
    public StaticViewPager(final Context context) {
        super(context);
    }

    /**
     * Initialize the view.
     *
     * @param context
     *            The application context.
     * @param attrs
     *            The requested attributes.
     */
    public StaticViewPager(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Make sure all are loaded at once
        final int childrenCount = getChildCount();
        setOffscreenPageLimit(childrenCount - 1);

        // Attach the adapter
        setAdapter(new PagerAdapter() {

            @Override
            public Object instantiateItem(final ViewGroup container, final int position) {
                return container.getChildAt(position);
            }

            @Override
            public boolean isViewFromObject(final View arg0, final Object arg1) {
                return arg0 == arg1;

            }

            @Override
            public int getCount() {
                return childrenCount;
            }

            @Override
            public void destroyItem(final View container, final int position, final Object object) {}
        });
    }

}

Lớp này không cần bộ điều hợp vì nó sẽ tải các khung nhìn từ bố cục. Để sử dụng nó, các dự án của bạn, chỉ cần sử dụng nó thay vì android.support.v4.view.ViewPager.

Tất cả những thứ ưa thích vẫn sẽ hoạt động, nhưng bạn không cần phải bận tâm với bộ điều hợp.


1
Điều này gần như làm việc cho tôi, nhưng tôi đã phải thay đổi onAttachedToWindow()để onFinishInflate().
ehehhh

@Eftekhari Sẽ dễ dàng hơn khi không có các đoạn và bạn viết ít mã hơn, điều đó có nghĩa là dễ đọc và dễ hiểu hơn về sau và ít bị lỗi hơn
Sabo

11

Dựa trên các câu trả lời trước, tôi đã thực hiện lớp sau để đạt được điều đó một cách đúng đắn và rõ ràng nhất (tôi hy vọng):

public class MyViewPagerAdapter extends PagerAdapter {

    ArrayList<ViewGroup> views;
    LayoutInflater inflater;

    public MyViewPagerAdapter(ActionBarActivity ctx){
        inflater = LayoutInflater.from(ctx);
        //instantiate your views list
        views = new ArrayList<ViewGroup>(5);
    }

    /**
     * To be called by onStop
     * Clean the memory
     */
    public void release(){
     views.clear();
        views = null;
    }

    /**
     * Return the number of views available.
     */
    @Override
    public int getCount() {
        return 5;
    }

    /**
     * Create the page for the given position. The adapter is responsible
     * for adding the view to the container given here, although it only
     * must ensure this is done by the time it returns from
     * {@link #finishUpdate(ViewGroup)}.
     *
     * @param container The containing View in which the page will be shown.
     * @param position The page position to be instantiated.
     * @return Returns an Object representing the new page. This does not
     *         need to be a View, but can be some other container of
     *         the page.  ,container
     */
    public Object instantiateItem(ViewGroup container, int position) {
        ViewGroup currentView;
        Log.e("MyViewPagerAdapter", "instantiateItem for " + position);
        if(views.size()>position&&views.get(position) != null){
            Log.e("MyViewPagerAdapter",
                  "instantiateItem views.get(position) " +
                  views.get(position));
            currentView = views.get(position);
        }
        else{
            Log.e("MyViewPagerAdapter", "instantiateItem need to create the View");
            int rootLayout = R.layout.view_screen;
            currentView = (ViewGroup) inflater.inflate(rootLayout, container, false);

            ((TextView)currentView.findViewById(R.id.txvTitle)).setText("My Views " + position);
            ((TextView)currentView.findViewById(R.id.btnButton)).setText("Button");
            ((ImageView)currentView.findViewById(R.id.imvPicture)).setBackgroundColor(0xFF00FF00);
        }
        container.addView(currentView);
        return currentView;
    }

    /**
     * Remove a page for the given position. The adapter is responsible
     * for removing the view from its container, although it only must ensure
     * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
     *
     * @param container The containing View from which the page will be removed.
     * @param position The page position to be removed.
     * @param object The same object that was returned by
     * {@link #instantiateItem(View, int)}.
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View)object);

    }

    /**
     * Determines whether a page View is associated with a specific key object
     * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is
     * required for a PagerAdapter to function properly.
     *
     * @param view   Page View to check for association with <code>object</code>
     * @param object Object to check for association with <code>view</code>
     * @return true if <code>view</code> is associated with the key object <code>object</code>
     */
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==((View)object);
    }
}

Và bạn phải đặt nó trong hoạt động của mình:

public class ActivityWithViewsPaged extends ActionBarActivity {

    /**
     * The page Adapter: Manage the list of views (in fact here, its fragments)
     * And send them to the ViewPager
     */
    private MyViewPagerAdapter pagerAdapter;

    /**
     * The ViewPager is a ViewGroup that manage the swipe from left
     * to right to left.
     * Like a listView with a gesture listener...
     */
    private ViewPager viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_views);

        // Find the viewPager
        viewPager = (ViewPager) super.findViewById(R.id.viewpager);

        // Instantiate the PageAdapter
        pagerAdapter = new MyViewPagerAdapter(this);

        // Affectation de l'adapter au ViewPager
        viewPager.setAdapter(pagerAdapter);
        viewPager.setClipToPadding(false);
        viewPager.setPageMargin(12);

        // Add animation when the page are swiped
        // this instanciation only works with honeyComb and more
        // if you want it all version use AnimatorProxy of the nineoldAndroid lib
        //@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            viewPager.setPageTransformer(true, new PageTransformer());
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        pagerAdapter.release();
    }

Trong đó các tệp XML là view_screen.xml rõ ràng:

<xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/screen"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

 <TextView
        android:id="@+id/txvTitle"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:shadowColor="#FF00FF"
        android:shadowDx="10"
        android:shadowDy="10"
        android:shadowRadius="5"
        android:textSize="32dp"
        android:textStyle="italic"
        android:background="#FFFFF000"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFF00F0">
        <TextView
            android:id="@+id/txvLeft"
            android:layout_width="wrap_content"
            android:layout_gravity="left"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/txvRight"
            android:layout_width="wrap_content"
            android:layout_gravity="right"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"/>
    </LinearLayout>
    <Button
        android:id="@+id/btnButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
    <ImageView
        android:id="@+id/imvPicture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"/>
</LinearLayout>

Và ActivtyMain có bố cục như sau:

<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="24dp"
    android:paddingRight="24dp"
    android:id="@+id/viewpager"
    android:background="#FF00F0F0">
</android.support.v4.view.ViewPager>

Cảm ơn Brian và Nicholas rất nhiều vì câu trả lời của bạn, tôi hy vọng tôi sẽ thêm một số thông tin rõ ràng nhất và làm sáng tỏ một số thực tiễn tốt cho tính năng này.


1
Không tốt lắm, bạn đang lưu tất cả các lượt xem đã tạo. Sẽ tốt hơn nếu nó chỉ lưu các chế độ xem hiển thị. (tương tự như convertview trong listview)
htafoya

4

Tôi muốn giải thích về câu trả lời của @Nicholas, bạn có thể nhận được các lượt xem theo id hoặc nếu chúng được thêm động, chỉ cần xem trực tiếp vị trí của nó

class WizardPagerAdapter extends PagerAdapter {

    public Object instantiateItem(View collection, int position) {

        View v = pager.getChildAt(position);

        return v;
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == ((View) arg1);
    }
}

4

Tôi muốn thêm giải pháp của tôi ở đây. Cho rằng bạn không cần phải mảnh sử dụng, bạn vẫn có thể tạo ra một PagerAdapterđộng gắn viewsthay vì fragmentsđến ViewPager.

Mở rộng PagerAdapterthay vìFragmentPagerAdapter

public class CustomPagerAdapter extends PagerAdapter {

  private Context context;

  public CustomPagerAdapter(Context context) {
    super();
    this.context = context;
  }


  @Override
  public Object instantiateItem(ViewGroup collection, int position) {
    LayoutInflater inflater = LayoutInflater.from(context);
    View view = null;
    switch (position){
      case 0:
        view = MemoryView.getView(context, collection);
        break;
      case 1:
        view = NetworkView.getView(context, collection);
        break;
      case 2:
        view = CpuView.getView(context, collection);
        break;
    }

    collection.addView(view);
    return view;
  }

  @Override
  public int getCount() {
    return 3;
  }

  @Override
  public boolean isViewFromObject(View view, Object object) {
    return view==object;
  }

  @Override
  public void destroyItem(ViewGroup collection, int position, Object view) {
    collection.removeView((View) view);
  }
}

Bây giờ bạn cần xác định ba lớp sẽ trả về viewsđược thổi phồng trong viewpager. Tương tự như CpuViewbạn sẽ có MemoryViewNetworkViewcác lớp. Mỗi người trong số họ sẽ thổi phồng bố trí tương ứng của họ.

public class CpuView {

public static View getView(Context context, ViewGroup collection) {

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context
        .LAYOUT_INFLATER_SERVICE);
    return inflater.inflate(R.layout.debugger_cpu_layout, collection, false);
  }
}

Và cuối cùng là một bố cục sẽ được thổi phồng trong mỗi khung nhìn

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000000"
        android:text="CPU"/>
</LinearLayout>

PS: Lý do tôi viết câu trả lời này là vì tất cả các giải pháp được cung cấp ở đây dường như đang hoạt động tốt, nhưng chúng đang làm tăng các bố cục trong chính lớp PagerAd CHƯƠNG. Đối với các dự án lớn, nó trở nên khó bảo trì nếu chúng có rất nhiều mã liên quan đến bố cục bị thổi phồng. Bây giờ trong ví dụ này, tất cả các khung nhìn có các lớp riêng biệt và bố cục riêng. Vì vậy, dự án có thể dễ dàng được duy trì.


IMO, các hoạt động dễ sử dụng hơn
Denny

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.