1

我已将 AOSP“Launcher2”WallpaperChooser 和 WallpaperChooserDialogFragment 中的代码改编为独立的图库应用程序,我希望它可以在 API 级别 7 到 API 16 上运行。

为了使其适用于 API 级别 < 11,我添加了 Android 支持库 (rev.10) “Compability Package”,V4 包。

我在兼容性包(android.support.v4.app.DialogFragment.java)中的更改基于此处的建议: DialogFragments with devices api level < 11

否则应用程序会因错误而崩溃(“DialogFragment 无法附加到容器视图”)。

但是,当部署到实际的 Jelly Bean 4.1.1 设备 (Nexus S) 时,该应用程序拒绝按预期工作,但它在模拟器上运行良好。

经过测试和工作:

  • Android 2.1 模拟器 (API 7)
  • Android 2.2 模拟器 (API 8)
  • Android 2.3.3 模拟器 (API 10)
  • Android 4.0.3 模拟器 (API 15)
  • Android 4.1 模拟器 (API 16)

  • Android 2.3.7 设备 (API 10)

已测试且不适用于:

  • Android 4.1.1 设备 (API 16)

在 4.1.1 设备上运行时,在水平视图中点击画廊图像时,它永远不会加载/显示画廊图像。

选择并旋转设备后才显示每张图片,然后再次选择新图片,再次旋转,即可正常显示。

我还没有设法弄清楚为什么它不能在真实设备上工作的问题,所以任何帮助表示赞赏。如果我删除了兼容性包并且只针对 API 级别 +11,则该应用程序可以在真正的 4.1.1 设备上运行。

这是应用程序的代码:

GalleryDemo.java:

package gallery.android.demo;

import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;

import android.os.Bundle;

public class GalleryDemo extends FragmentActivity {

@Override
public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.wallpaper_chooser_base);

    Fragment fragmentView = getSupportFragmentManager().findFragmentById(R.id.wallpaper_chooser_fragment);
    if (fragmentView == null) {
        DialogFragment fragment = GalleryDemoDialogFragment.newInstance();
        fragment.show(getSupportFragmentManager(), "dialog");
    }
}
}

GalleryDemoDialogFragment.java:

package gallery.android.demo;

import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;

import android.annotation.TargetApi;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;
import android.widget.Toast;

import java.util.ArrayList;

@SuppressWarnings("deprecation")
@TargetApi(16)
public class GalleryDemoDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener {

private static final String TAG = "GalleryDemoDialogFragment";
private static final String EMBEDDED_KEY = "gallery.android.demo.GalleryDemoDialogFragment.EMBEDDED_KEY";

private boolean mEmbedded;
private Bitmap mBitmap = null;

private ArrayList<Integer> mThumbs;
private ArrayList<Integer> mImages;
private WallpaperLoader mLoader;
private WallpaperDrawable mWallpaperDrawable = new WallpaperDrawable();

public static GalleryDemoDialogFragment newInstance() {
    GalleryDemoDialogFragment fragment = new GalleryDemoDialogFragment();
    fragment.setCancelable(true);
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null && savedInstanceState.containsKey(EMBEDDED_KEY)) {
        mEmbedded = savedInstanceState.getBoolean(EMBEDDED_KEY);
    } else {
        mEmbedded = isInLayout();
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putBoolean(EMBEDDED_KEY, mEmbedded);
}

private void cancelLoader() {
    if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
        mLoader.cancel(true);
        mLoader = null;
    }
}

@Override
public void onDetach() {
    super.onDetach();
    cancelLoader();
}

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

@Override
public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    FragmentActivity activity = getActivity();
    if (activity != null) {
        activity.finish();
    }
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    findWallpapers();
    return null;
}

@TargetApi(16)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    findWallpapers();
    if (mEmbedded) {
        View view = inflater.inflate(R.layout.wallpaper_chooser, container, false);
        switch (Build.VERSION.SDK_INT) {
        case Build.VERSION_CODES.ICE_CREAM_SANDWICH :
            view.setBackgroundDrawable(mWallpaperDrawable);
            break;
        case Build.VERSION_CODES.JELLY_BEAN :
            view.setBackground(mWallpaperDrawable);
            break;
        default:
            view.setBackgroundDrawable(mWallpaperDrawable);
            break;
        }

        final Gallery gallery = (Gallery) view.findViewById(R.id.gallery);
        gallery.setCallbackDuringFling(false);
        gallery.setOnItemSelectedListener(this);
        gallery.setAdapter(new ImageAdapter(getActivity()));

        View setButton = view.findViewById(R.id.set);
        setButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = gallery.getSelectedItemPosition();
                String[] wallpaperArray = getResources().getStringArray(R.array.wallpapers);
                String name = wallpaperArray[position]+".jpg";

                Log.i(TAG, "Position="+position);
                Log.i(TAG, "Filename="+name);
                Toast.makeText(getActivity().getApplicationContext(), "(Save2SD)\n"+
                                                                      "Position="+position+"\n"+
                                                                      "Filename="+name+
                                                                      "", Toast.LENGTH_SHORT).show();
            }
        });
        return view;
    }
    return null;
}


@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}

@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
        mLoader.cancel();
    }
    Log.d(TAG, "onItemSelected()");
    mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
}

@Override
public void onNothingSelected(AdapterView<?> parent) {
}

private void findWallpapers() {
    mThumbs = new ArrayList<Integer>(24);
    mImages = new ArrayList<Integer>(24);

    final Resources resources = getResources();
    final String packageName = resources.getResourcePackageName(R.array.wallpapers);

    addWallpapers(resources, packageName, R.array.wallpapers);
    addWallpapers(resources, packageName, R.array.extra_wallpapers);
}

private void addWallpapers(Resources resources, String packageName, int list) {
    final String[] extras = resources.getStringArray(list);
    for (String extra : extras) {
        int res = resources.getIdentifier(extra, "drawable", packageName);
        if (res != 0) {
            final int thumbRes = resources.getIdentifier(extra + "_small", "drawable", packageName);
            if (thumbRes != 0) {
                mThumbs.add(thumbRes);
                mImages.add(res);
            }
        }
    }
}


private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
    private LayoutInflater mLayoutInflater;

    ImageAdapter(FragmentActivity activity) {
        mLayoutInflater = activity.getLayoutInflater();
    }

    public int getCount() {
        return mThumbs.size();
    }

    public Object getItem(int position) {
        return position;
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View view;

        if (convertView == null) {
            view = mLayoutInflater.inflate(R.layout.wallpaper_item, parent, false);
        } else {
            view = convertView;
        }

        ImageView iv = (ImageView) view.findViewById(R.id.wallpaper_image);

        int thumbRes = mThumbs.get(position);
        iv.setImageResource(thumbRes);

        Drawable thumbDrawable = iv.getDrawable();
        if (thumbDrawable != null) {
            thumbDrawable.setDither(true);
        } else {
            Log.e(TAG, "Error decoding thumbnail resId=" + thumbRes + " for wallpaper #" + position);
        }
        return view;
    }
}


private class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
    BitmapFactory.Options mOptions;

    WallpaperLoader() {
        mOptions = new BitmapFactory.Options();
        mOptions.inDither = false;
        mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
    }

    @Override
    protected Bitmap doInBackground(Integer... params) {
        if (isCancelled()) return null;
        try {
            return BitmapFactory.decodeResource(getResources(), mImages.get(params[0]), mOptions);
        } catch (OutOfMemoryError e) {
            return null;
        }
    }

    @Override
    protected void onPostExecute(Bitmap b) {
        if (b == null) return;
        if (!isCancelled() && !mOptions.mCancel) {
            if (mBitmap != null) {
                mBitmap.recycle();
            }

            View v = getView();
            if (v != null) {
                Log.d(TAG, "WallpaperLoader.onPostExecute() getView != null");
                mBitmap = b;
                mWallpaperDrawable.setBitmap(b); // Doesn't appear to work on (physical) device with Jelly Bean (4.1.1)?
                v.postInvalidate();
            } else {
                Log.d(TAG, "WallpaperLoader.onPostExecute(Bitmap b) getView == null");
                mBitmap = null;
                mWallpaperDrawable.setBitmap(null);
            }
            mLoader = null;
        } else {
           b.recycle();
        }
    }

    void cancel() {
        mOptions.requestCancelDecode();
        super.cancel(true);
    }
}

private static class WallpaperDrawable extends Drawable {

    Bitmap mBitmap;
    int mIntrinsicWidth;
    int mIntrinsicHeight;

    void setBitmap(Bitmap bitmap) {
        Log.d(TAG, "WallpaperDrawable.setBitmap(Bitmap)");
        mBitmap = bitmap;
        if (mBitmap == null) {
            Log.d(TAG, "WallpaperDrawable.setBitmap(Bitmap) mBitmap == null");
            return;
        }
        mIntrinsicWidth = mBitmap.getWidth();
        mIntrinsicHeight = mBitmap.getHeight();
    }

    @Override
    public void draw(Canvas canvas) {
        if (mBitmap == null) {
            Log.d(TAG, "WallpaperDrawable.draw(Canvas) mBitmap == null");
            return;
        }
        Log.d(TAG, "WallpaperDrawable.draw(Canvas)");
        int width = canvas.getWidth();
        int height = canvas.getHeight();
        int x = (width - mIntrinsicWidth) / 2;
        int y = (height - mIntrinsicHeight) / 2;
        canvas.drawBitmap(mBitmap, x, y, null);
    }

    @Override
    public int getOpacity() {
        return android.graphics.PixelFormat.OPAQUE;
    }

    @Override
    public void setAlpha(int alpha) {
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
    }
}
}
4

1 回答 1

1

运行 API 16 的真实设备是唯一具有硬件加速渲染的设备,这就是它表现不同的原因。您可以尝试在 Activity 的清单条目中设置 android:hardwareAccelerated="false" 来进行试验。

如果在更改 Drawable 的内容时调用 invalidateSelf(),则可以保持加速:

private static class WallpaperDrawable extends Drawable {
    void setBitmap(Bitmap bitmap) {
        . . .            
        invalidateSelf();
    }
}
于 2012-09-10T00:02:40.247 回答