7

で画像をキャプチャするチュートリアルからコピーしたアプリがありMediaStore.ACTION_IMAGE_CAPTUREます。携帯電話でアプリを実行すると、奇妙なことが起こります。

電話を動かしていなくても、操作中にカメラアプリ自体の向きが数回反転しています。チュートリアル アプリに戻る前に、簡単にランドスケープ モードになります。その結果、チュートリアル アプリはコントロールが戻った後に縦向きモードに戻り、画像が失われます。カメラ アクティビティの向きを横向きに設定してみましたが、画像は失われません。

ただし、アプリのレイアウトは縦向きモードを対象としています。または、写真をキャプチャするときにカメラを横向きに保持すると、アプリがフォーカスに戻った後に電話を回すことができ、画像を失うことはありません.

私はウェブ上でいくつかのことをしました。Stackoverflow の誰かが、方向の変更により への追加の呼び出しが発生したと述べましたonCreate。「onCreate()呼び出される理由は、縦向きのときにカメラ アクティビティを呼び出すと、向きが変更され、以前のアクティビティが破棄されるためです。」onCreate とonActivityResultメソッドにブレークポイントを設定して、アプリをデバッグ モードで実行しました。onCreateポートレートモードで写真を撮ると呼び出されるのは確かです。呼び出しの順序は、onCreate、onActivityResult、onCreate です。横向きモードで写真を撮ると (どちらの方法でもカメラ アプリが終了します)、onCreate は呼び出されません。何が起こっているのかがわかったので、どうすればそれが問題にならないようにできますか? アプリは次のようになります。

package com.example.testapp;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;

public class CameraActivity extends Activity implements View.OnClickListener {

    ImageButton ib;
    Button b;
    ImageView iv;
    Intent i;
    final static int cameraData = 0;
    Bitmap bmp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.photo_activity);
        initialize();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        // TODO Auto-generated method stub
        super.onConfigurationChanged(newConfig);
        setContentView(R.layout.photo_activity);
        initialize();
    }

    private void initialize() {
        iv = (ImageView)findViewById(R.id.imageViewReturnedPicture);
        ib = (ImageButton)findViewById(R.id.imageButtonTakePicture);
        b = (Button)findViewById(R.id.buttonSetWallpaper);
        b.setOnClickListener(this);
        ib.setOnClickListener(this);
    }

    @Override
    public void onClick(View arg0) {
        switch (arg0.getId()) {

        case R.id.buttonSetWallpaper:
            try {
                WallpaperManager wm = WallpaperManager.getInstance(getApplicationContext());
                wm.setBitmap(bmp);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;

        case R.id.imageButtonTakePicture:
            i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(i, cameraData);
            break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            bmp = (Bitmap)extras.get("data");
            iv.setImageBitmap(bmp);
        }
    }
}

そして、これが私がこの活動のマニフェストに持っているものです:

                android:name="com.example.testapp.CameraActivity"
                android:label="カメラ アクティビティ"
                Android:configChanges="向き"
                android:screenOrientation="ポートレート"

かなりの検索を行いましたが、見つけたものの多くには具体的な例がありません。使用する機能だけでなく、コードがどのように見えるかを知る必要があります。

私の電話はLGモーションです。他の誰かがこの問題に遭遇しましたか? どうすれば修正できますか?

4

7 に答える 7

15

マニフェストでは、各アクティビティで configChanges を使用します。

 <activity
        android:name=".MainActivity"
        android:configChanges="screenLayout|orientation|screenSize">

これがお役に立てば幸いです。

于 2015-07-20T12:04:20.257 に答える
8

onRetainNonConfigurationInstanceをオーバーライドし、 getLastNonConfigurationInstanceを使用してビットマップを保存/復元する必要があります。

このような:

// during onCreate(Bundle), after initialise()
bmp = getLastNonConfigurationInstance();
if(bmp!=null){ iv.setImageBitmap(bmp); }
else { /* the image was not taken yet */ }

次に、あなたのアクティビティでオーバーライドします:

@Override
public Object onRetainNonConfigurationInstance (){
     return bmp;
}

これにより、回転中にビットマップが「保存」されます。

編集:

推奨される onSaveInstanceState を使用した例は動作しますが、大量のメモリを使用し、非常に遅くなるためお勧めできませんが、すぐに他の状況で必要になります。

public class SomethingSomething extends Activity{

    String name="";
    int page=0;

    // This is called when the activity is been created
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

            // if you saved something on outState you can recover them here
            if(savedInstanceState!=null){
                name = savedInstanceState.getString("name");
                page = savedInstanceState.getInt("page");
            }
    }

    // This is called before the activity is destroyed
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

            outState.putString("name", name);
            outState.putInt("page", page);
    }
}

ご覧のとおり、このソリューションがあなたのケースに適していない理由は、これに使用されるAndroid バンドルが、Parcelable を実装するプリミティブ、文字列、およびクラスを処理できる Android の特別なタイプのシリアル化であるためです (これらのクラスは実際にはプリミティブのみをパーセルします)。 . また、Bitmap が Parcelable を実装していても、Bitmap のすべてのバイトをバンドルにコピーするには多くの時間がかかり、Bitmap のすでに大きなメモリ消費量が 2 倍になります。

を使用したソリューションを見てみましょうsetRetainInstance( にある例から少しコピーしたものです\sdk\extras\android\support\samples\Support4Demos\src\com\example\android\supportv4\app\FragmentRetainInstanceSupport.Java

他のいくつかの凝ったトリックを示しているので、例も確認してください。

// This fragment will be managed by the framework but we won't built a UI for it.
public class FragRetained extends Fragment{
   public static final String TAG = "TAG.FragRetained";
   private Bitmap bitmap;

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

        // Tell the framework to try to keep this fragment around
        // during a configuration change.
        setRetainInstance(true);
    }
    public Bitmap getBitmap() { return bitmap; }
    public void setBitmap(Bitmap bmp) { bitmap = bmp; }
}

public class MyActivity extends Activity{

    private FragRetained myFragRetained;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
            // set the content view
            img = (ImageView)findViewById(R.id.byImgV);


            myFragRetained = getFragmentManger.findFragmentByTag(FragRetained.TAG);
            if(myFragRetained == null){
                myFragRetained = new FragRetained ();
            }else{
               Bitmap b = myFragRetained.getBitmap();
               if(b==null){
                  // the user still did not choose the photo
               }else{
                  img.setImageBitmap(b);
               }
            }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            bmp = (Bitmap)extras.get("data");
            iv.setImageBitmap(bmp);
            myFragRetained.setBitmap(bmp);
        }
    }

}

マニフェストから行を削除してandroid:configChanges="orientation"ください。おそらく、良いことよりも害を及ぼす可能性が高いためです。それについての小さな引用です。

注: この属性の使用は避け、最後の手段としてのみ使用してください。構成の変更による再起動を適切に処理する方法の詳細については、ランタイムの変更の処理を参照してください。

于 2013-02-23T17:35:47.957 に答える
3

月曜日にモバイル開発グループの会議に行き、ヒントを得ました。大手企業のさまざまなプラットフォーム用のアプリを作成するプログラマーは、アプリケーション オブジェクトにビットマップを添付することを提案しました。それをどのように行うかを検討したところ、次のブログ投稿の提案に基づいて、最終的に機能するソリューション (投稿の最後にある新しい質問) を思いつきました: http://android-er.blogspot.com/2011/ 10/share-bitmap-between-activities-as.html

基本的に、これには、アプリ内の任意のアクティビティがアクセスできる場所にある共通リソースとしてビットマップを設定する必要がありました。次のように新しいクラスを作成しました。

package com.example.testapp;
 
import android.graphics.Bitmap;
 
public class CommonResources {
        public static Bitmap cameraBmp = null;
}

次に、CameraActivity.java 内の bmp へのすべての参照を CommonResources.cameraBmp に変更しました。

初期化中に CommonResources.cameraBmp が null でない場合は、それを使用して ImageView にビットマップを設定します。

onCreate と initialize は次のようになります。

@Override
protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
              super.onCreate(savedInstanceState);
        setContentView(R.layout.photo_activity);
        initialize();
}

private void initialize() {
        iv = (ImageView)findViewById(R.id.imageViewReturnedPicture);
        ib = (ImageButton)findViewById(R.id.imageButtonTakePicture);
        b = (Button)findViewById(R.id.buttonSetWallpaper);
        if (CommonResources.cameraBmp != null) {
                   iv.setImageBitmap(CommonResources.cameraBmp);
           }
        b.setOnClickListener(this);
        ib.setOnClickListener(this);
}

次に、少し整理するために、次のように onPause を追加しました。

@Override
protected void onPause() {
        // TODO Auto-generated method stub
              super.onPause();
        if (isFinishing()) {
                   // Save the bitmap.
                        CommonResources.cameraBmp = null;
           }
}

私の大きな疑問は次のとおりです。メモリを適切に処理したか、またはガベージ コレクションの問題を作成しましたか?

于 2013-03-01T23:13:13.523 に答える
1

簡単な解決策を求めてウェブをかなり長い間検索した後、いくつかのコードを見て、これを思いつきました。将来このスレッドを見る人の助けになることを願っています!

Activity クラスにグローバル変数がありますpublic Uri imageURI = null;。これは、使用している ImaveView の画像 URI を設定するために使用されます。onCreate()との次のコードonSaveInstanceState()

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

    //restore the bitmap for the image
    if (savedInstanceState!=null)
    {
        imageURI=savedInstanceState.getParcelable("imageURI");
        ImageView photo=(ImageView)findViewById(R.id.image);
        photo.setImageURI(imageURI);
    }
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putParcelable("imageURI", imageURI);
}

誰かがこれを役に立てば幸いです。詳細が必要かどうか尋ねてください。

于 2014-05-05T08:59:37.400 に答える
1

向きの変更を処理する方法は、インスタンスの状態を保存することです。onSaveInstanceStateメソッドに入力すると、そのバンドルに保存したデータを onCreate 中に戻すことができます。これはビューのために行われますが、他のデータは自分で保存する必要があります。BitMap を含む、プリミティブ、パーセル可能、またはシリアライズ可能なオブジェクトは、この方法で保存できます。

これは、構成の変更を乗り切るためだけでなく、メモリ不足の状態で状態を維持するためにも行う必要があります。

于 2013-02-23T17:19:38.290 に答える
1

android:launchMode="singleTop" を android menifest のアクティビティに追加してみましたか? 問題は、向きが変わってデータが失われたときに Android がアクティビティを再起動することです。これにより、アクティビティが既に実行されている場合、新しいインスタンスは作成されません。

        <activity 
            android:name=".FacebookActivity" 
            android:label="@string/facebook_app_name"
            android:launchMode="singleTop"
            android:configChanges="keyboardHidden|orientation"
            >
        </activity>
于 2013-02-23T17:23:10.617 に答える
0

onSaveInstanceStateを使用してから onCreate で取得した引数を使用するか、configChanges を使用して向きの変更を処理することをマニフェストに設定し、自分で処理するケースを設定します (たとえば、 orientation|screenSize|screenLayout ) 。

于 2013-02-23T17:25:10.553 に答える