将prev / next-buttons添加到“detail fragment”(主/详细活动)

时间:2015-10-13 15:34:19

标签: android gridview android-cursorloader

我想在主/详细视图中添加“上一个/下一个”功能。主人加载GridView(带缩略图),当点击时 - 通过URI(基本上包含相应的图像ID)启动DetailActivity。

我想要的是细节活动/片段能够直接跳转到上一个/下一个图像 - 而不必在主视图中来回移动。理想情况下,我想捕捉滑动手势。

我已经读过,最好的方法是使用ViewPager,使用FragmentStatePagerAdapter来保存子视图。但是,我想了解为什么我的第一次尝试不起作用(下面)。(另外,我希望避免重复修改代码,除非我必须这样做。)虽然我的尝试 工作,我只在几次滑动后得到OutOfMemory错误/崩溃:

ldap.base.provider.url.1=
ldap.base.dn.1=
ldap.security.principal.1=
ldap.security.credentials.1=
ldap.auth.search.filter.1=
ldap.user.mappings.1=

以下是相关的(我希望)代码。

  • 请注意来自 MainActivity 10-13 13:11:21.441 4149-4149/com.example.android.galleri.app E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 33177612 byte allocation with 12795712 free bytes and 12MB until OOM" 10-13 13:11:21.455 4149-4149/com.example.android.galleri.app I/art﹕ Clamp target GC heap from 195MB to 192MB 10-13 13:11:21.456 4149-4149/com.example.android.galleri.app I/art﹕ Alloc partial concurrent mark sweep GC freed 6(192B) AllocSpace objects, 0(0B) LOS objects, 6% free, 179MB/192MB, paused 903us total 10.248ms 10-13 13:11:21.474 4149-4149/com.example.android.galleri.app I/art﹕ Clamp target GC heap from 195MB to 192MB 10-13 13:11:21.474 4149-4149/com.example.android.galleri.app I/art﹕ Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 6% free, 179MB/192MB, paused 975us total 18.144ms 10-13 13:11:21.474 4149-4149/com.example.android.galleri.app I/art﹕ Forcing collection of SoftReferences for 31MB allocation 10-13 13:11:21.490 4149-4149/com.example.android.galleri.app I/art﹕ Clamp target GC heap from 195MB to 192MB 10-13 13:11:21.490 4149-4149/com.example.android.galleri.app I/art﹕ Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 6% free, 179MB/192MB, paused 1.060ms total 15.295ms 10-13 13:11:21.490 4149-4149/com.example.android.galleri.app E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 33177612 byte allocation with 12795712 free bytes and 12MB until OOM" 10-13 13:11:21.490 4149-4149/com.example.android.galleri.app D/skia﹕ --- decoder->decode returned false 10-13 13:11:21.491 4149-4149/com.example.android.galleri.app D/AndroidRuntime﹕ Shutting down VM --------- beginning of crash 10-13 13:11:21.492 4149-4149/com.example.android.galleri.app E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.example.android.galleri.app, PID: 4149 java.lang.OutOfMemoryError: Failed to allocate a 33177612 byte allocation with 12795712 free bytes and 12MB until OOM at dalvik.system.VMRuntime.newNonMovableArray(Native Method) at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635) at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611) at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391) at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:417) at com.example.android.galleri.app.DetailFragment.onLoadFinished(DetailFragment.java:302) at com.example.android.galleri.app.DetailFragment.onLoadFinished(DetailFragment.java:43) at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:427) at android.support.v4.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(LoaderManager.java:395) at android.support.v4.content.Loader.deliverResult(Loader.java:104) at android.support.v4.content.CursorLoader.deliverResult(CursorLoader.java:73) at android.support.v4.content.CursorLoader.deliverResult(CursorLoader.java:35) at android.support.v4.content.AsyncTaskLoader.dispatchOnLoadComplete(AsyncTaskLoader.java:223) at android.support.v4.content.AsyncTaskLoader$LoadTask.onPostExecute(AsyncTaskLoader.java:61) at android.support.v4.content.ModernAsyncTask.finish(ModernAsyncTask.java:461) at android.support.v4.content.ModernAsyncTask.access$500(ModernAsyncTask.java:47) at android.support.v4.content.ModernAsyncTask$InternalHandler.handleMessage(ModernAsyncTask.java:474) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:211) at android.app.ActivityThread.main(ActivityThread.java:5389) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815) 来电,它会将图片正常加载到DetailFragment中。

  • 此方法从 PhotoFragment 调用(通过回调/接口):GridView的public void onItemSelected(Uri contentUri)处理程序,使用onClick方法创建。这是正常的主图像加载图像,工作正常。

  • 然后另一种方式: DetailActivity 捕获滑动手势,并调用DetailFragment中的onCreate方法(通过成员对象/引用)。

  • DetailFragment的 changeImage方法通过Utility类(容器对象)获取PhotoFragment中使用的游标。然后,它获取下一个/上一行并构造一个具有该ID的新URI,并使用该URI启动(重新启动?)DetailActivity。

这些重启似乎会累积内存,直到应用程序崩溃。崩溃(OOM错误)总是发生在DetailFragment的同一行,在我将Bitmap加载到PhotoView的部分:

changeImage

MainActivity.java

// OOM crash always occurs HERE (after ~5 swipes)
thumbBitmap = BitmapFactory.decodeFile(thumbData);  

PhotoFragment.java

public class MainActivity extends ActionBarActivity implements PhotoFragment.Callback  {

    private static final String DETAILFRAGMENT_TAG = "DFTAG";
    private boolean mTwoPane;
    private PhotoFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (findViewById(R.id.photo_detail_container) != null) {
            mTwoPane = true;

            if (savedInstanceState == null) {
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.photo_detail_container, new DetailFragment(), DETAILFRAGMENT_TAG)
                        .commit();
            }
        } else {
            mTwoPane = false;
        }

        mFragment = ((PhotoFragment) getSupportFragmentManager()
                .findFragmentById(R.id.fragment_photo));
        mFragment.setTwoPaneUI(mTwoPane);

    }


    @Override
    // this method is called from PhotoFragment (GridView) via a callback interface
    public void onItemSelected(Uri contentUri) {

        if (mTwoPane) {
            Bundle args = new Bundle();
            args.putParcelable(DetailFragment.DETAIL_URI, contentUri);

            DetailFragment fragment = new DetailFragment();
            fragment.setArguments(args);

            fragment.setTwoPane(true);

            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.photo_detail_container, fragment, DETAILFRAGMENT_TAG)
                    .commit();
        } else {

            // launch DetailActivity
            Intent intent = new Intent(this, DetailActivity.class)
                    .setData(contentUri);
            startActivity(intent);
        }
    }

}

DetailActivity.java

public class PhotoFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

    private final static int LOADER_ID = 87;
    private PhotoAdapter mPhotoAdapter;
    private int mPosition;
    private GridView mGridView;
    private TextView mEmptyView;
    private int count_mem = -1;
    private static final String SELECTED_KEY = "POSITION";
    private boolean mTwoPane;
    private boolean DF_hidden = false;

    // these are the data we want from MediaStore
    private final static String[] THUMBNAIL_COLUMNS = {
            //PhotoContract.ThumbEntry.COLUMN_THUMB_ID,
            PhotoContract.ThumbEntry.COLUMN_THUMB_DATA,
            PhotoContract.ThumbEntry.COLUMN_IMAGE_PK,
            PhotoContract.ThumbEntry.COLUMN_TITLE,
            PhotoContract.ThumbEntry.COLUMN_DESC,
            PhotoContract.ThumbEntry.COLUMN_DATE,
            PhotoContract.ThumbEntry.COLUMN_FILENAME,
            PhotoContract.ThumbEntry.COLUMN_DATA,
            PhotoContract.ThumbEntry.COLUMN_IMAGESEQUENCE
    };


    static final int COL_THUMB_DATA = 0;
    static final int COL_IMAGE_ID = 1;
    static final int COL_TITLE = 2;
    static final int COL_DESC = 3;
    static final int COL_DATE = 4;
    static final int COL_FILENAME = 5;
    static final int COL_DATA = 6;
    static final int COL_IMAGESEQUENCE = 7;

    public void setTwoPaneUI(boolean pTwoPane) {
        mTwoPane = pTwoPane;
    }

    // interfaces
    public interface Callback {
        public void onItemSelected(Uri photoUri);
    }
    public interface FragmentCallback {
        public void onTaskDone(int count);
    }


    public PhotoFragment() {
    }

    public int getPosition() {
       return mPosition;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        getLoaderManager().initLoader(LOADER_ID, null, this);
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    public void restartLoader() {
        getLoaderManager().restartLoader(LOADER_ID, null, this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (mPosition != GridView.INVALID_POSITION) {
            outState.putInt(SELECTED_KEY, mPosition);
        }
        super.onSaveInstanceState(outState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);

        mPhotoAdapter = new PhotoAdapter(getActivity(), null, 0);
        GridView gridView = (GridView) rootView.findViewById(R.id.listview_photo);
        gridView.setAdapter(mPhotoAdapter);

        mEmptyView = (TextView) rootView.findViewById(R.id.list_empty);
        mGridView = gridView;

        // handle user clicking on an image
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                Cursor cursor = (Cursor) parent.getItemAtPosition(position);

                if (cursor != null) {
                    Uri baseUri = PhotoContract.PhotoEntry.buildPhotoUriWithId(cursor.getLong(COL_IMAGE_ID));
                    Log.v("gallery", "PhotoFragment.onItemClick() image_id = " + cursor.getLong(COL_IMAGE_ID)
                           + ", baseUri = " + baseUri.toString());

                    // make sure detail fragment is visible
                    if (mTwoPane) showDetailFragment();
                    else Utility.setPosition(position);  // else (single-pane): store position clicked!

                    ((Callback)getActivity()).onItemSelected(baseUri);
                }
                mPosition = position;
            }
        });

        if (savedInstanceState != null && savedInstanceState.containsKey(SELECTED_KEY)) {
            mPosition = savedInstanceState.getInt(SELECTED_KEY);
        }

        return rootView;
    }


    @Override
    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {

        Uri thumbs_uri = PhotoContract.ThumbEntry.CONTENT_URI;
        return new CursorLoader(getActivity(), thumbs_uri, THUMBNAIL_COLUMNS,
                null,null,  // read everything (all thumbnails)
                null
               );
    }

    @Override
    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
        if (cursor != null) {

            // register observer onto cursor
            cursor.setNotificationUri(getActivity().getContentResolver(), PhotoContract.ThumbEntry.CONTENT_URI);

            mPhotoAdapter.swapCursor(cursor);
            if (mPosition != GridView.INVALID_POSITION) {
                mGridView.setSelection(mPosition);  // scroll into view
            }
        }

        // save (set) cursor in Utility
        Utility.setCursor(cursor);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> cursorLoader) {
        mPhotoAdapter.swapCursor(null);
    }
}

DetailFragment.java

public class DetailActivity extends ActionBarActivity  {

    private static final String DETAILFRAGMENT_TAG = "DFTAG";
    private float x1,x2, y1,y2;
    static final int MIN_DISTANCE = 150;
    private DetailFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        if (savedInstanceState == null) {

            Bundle arguments = new Bundle();
            arguments.putParcelable(DetailFragment.DETAIL_URI, getIntent().getData());

            //DetailFragment fragment = new DetailFragment();
            mFragment = new DetailFragment();
            mFragment.setArguments(arguments);

            getSupportFragmentManager().beginTransaction()
                    .add(R.id.photo_detail_container, mFragment, DETAILFRAGMENT_TAG)
                    .commit();
        }
    }



    // http://stackoverflow.com/questions/6645537/how-to-detect-the-swipe-left-or-right-in-android
    @Override
    public boolean onTouchEvent(MotionEvent event)
    {

        switch(event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                x1 = event.getX();
                y1 = event.getY();
                break;
            case MotionEvent.ACTION_UP:
                x2 = event.getX();
                y2 = event.getY();

                float deltaX = x2 - x1;
                if (Math.abs(deltaX) > MIN_DISTANCE) {

                    if (x2 > x1)
                        mFragment.changeImage(-1);  // move backwards
                    else
                        mFragment.changeImage(1);  // move forwards
                }
                break;
        }
        return super.onTouchEvent(event);
    }






    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_detail, menu);
        return true;
    }

}

0 个答案:

没有答案