在片段之间切换时如何处理相机?

Posted

技术标签:

【中文标题】在片段之间切换时如何处理相机?【英文标题】:How to handle camera while switching between fragments? 【发布时间】:2017-10-16 16:35:53 【问题描述】:

我正在使用 2 个按钮在 2 个不同的片段之间切换。一个片段有一个相机视图并且第一次打开得很好,但是当从第二个片段再次返回到同一个片段时会出现一个空白屏幕。尽管相机硬件是免费的,但看不到相机预览。我没有使用 camera2 api,但需要解决这个错误。我添加了完整的代码和图像以寻求帮助。

MANifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.root.meeransunday" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar"
            android:screenOrientation="portrait"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <supports-screens android:smallScreens="true"
            android:normalScreens="true"
            android:largeScreens="true"
            android:xlargeScreens="true"
            android:anyDensity="true"
            android:resizeable="true"/>

    </application>
    <activity android:name="meeranSunday"
        android:configChanges="orientation|keyboardHidden"
        />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.CAMERA">

    </uses-permission>

    <uses-feature android:name="android.hardware.camera.autofocus" />

    <uses-feature android:name="android.hardware.camera" />
</manifest>

片段java类

public class scan extends Fragment implements ZXingScannerView.ResultHandler 
private ZXingScannerView zXingScannerView;
private SurfaceView mySurfaceView;
private Camera mCamera;
private CameraPreview mPreview;
private String m_Text = "";
private String number = "";

private OnFragmentInteractionListener mListener;

public scan() 
@Override
public void handleResult(Result rawResult) 
    // Do something with the result here

    Log.e("handler", rawResult.getText()); // Prints scan results
    Log.e("handler", rawResult.getBarcodeFormat().toString()); // Prints the scan format (qrcode)

    // show the scanner result into dialog box.
    AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
    builder.setTitle("Scan Result");
    builder.setMessage(rawResult.getText());
    number = rawResult.getText().substring(rawResult.getText().length() - 13);

    //
    final EditText input = new EditText(this.getActivity());
    // Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
    input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
    builder.setView(input);

    // Set up the buttons
    builder.setPositiveButton("OK", new DialogInterface.OnClickListener() 
        @Override
        public void onClick(DialogInterface dialog, int which) 
            m_Text = input.getText().toString();

            try 
                SmsManager smsManager = SmsManager.getDefault();
                smsManager.sendTextMessage(number, null, m_Text + " Transferred To Your Account From MTM Account", null, null);

             catch (Exception e) 

                e.printStackTrace();
            
        
    );
    builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() 
        @Override
        public void onClick(DialogInterface dialog, int which) 
            dialog.cancel();
        
    );

    builder.setNegativeButton("NO",
        new DialogInterface.OnClickListener() 
            public void onClick(DialogInterface dialog, int which) 
                dialog.cancel();
            
        );




    //
    AlertDialog alert1 = builder.create();
    alert1.show();


    // If you would like to resume scanning, call this method below:
    zXingScannerView.resumeCameraPreview(this);


// TODO: Rename and change types and number of parameters
public static scan newInstance() 
    scan fragment = new scan();
    Bundle args = new Bundle();
    fragment.setArguments(args);
    return fragment;


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


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

    if (checkCameraHardware(getActivity().getApplicationContext())) 
        Toast.makeText(this.getActivity(), "camera hardware is free", Toast.LENGTH_SHORT).show();
        zXingScannerView = new ZXingScannerView(this.getActivity().getApplicationContext());
        zXingScannerView.setResultHandler(this);
        zXingScannerView.startCamera(0);
     else 

        Toast.makeText(this.getActivity(), "camera hardware is NOT free", Toast.LENGTH_SHORT).show();
    

    FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview);
    preview.addView(zXingScannerView);
    return view;

@Override
public void onPause() 
    super.onPause();
    try 
        mCamera = Camera.open();
     catch (RuntimeException e) 
        Log.d("camera opening attempt:", e.getMessage());
     finally 
        if (mCamera != null) 
            Log.d("camera opening attempt:", "yups ");
            mCamera.release();
            zXingScannerView.stopCamera();
            zXingScannerView.stopCameraPreview();
        
    

@Override
public void onResume() 
    super.onResume();
    zXingScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
    zXingScannerView.startCamera(0); // Start camera on resume


public boolean checkCameraHardware(Context context) 
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) 

        return true;
     else 

        return false;
    



public void onButtonPressed(Uri uri) 
    if (mListener != null) 
        mListener.onFragmentInteraction(uri);
    

@Override

public void onAttach(Context context) 
    super.onAttach(context);
    if (context instanceof OnFragmentInteractionListener) 
        mListener = (OnFragmentInteractionListener) context;
     else 
        throw new RuntimeException(context.toString() +
            " must implement OnFragmentInteractionListener");
    

@Override
public void onDetach() 
    Log.d("aa", "sdsdssdsddssssd");
    try 
        mCamera = Camera.open();
     catch (RuntimeException e) 
        Log.d("camera opening attempt:", e.getMessage());
     finally 
        if (mCamera != null) 
            mCamera.release();
            zXingScannerView.stopCamera();
            zXingScannerView.stopCameraPreview();
        
    

    super.onDetach();
    mListener = null;


public interface OnFragmentInteractionListener 
    // TODO: Update argument type and name\

    void onFragmentInteraction(Uri uri);

MAinactivity

public class MainActivity extends AppCompatActivity
 implements NavigationView.OnNavigationItemSelectedListener 
 public int check = 0;

 @Override
 protected void onCreate(Bundle savedInstanceState) 
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
     setSupportActionBar(toolbar);
     DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
     ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
         this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
     drawer.setDrawerListener(toggle);
     toggle.syncState();

     NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
     navigationView.setNavigationItemSelectedListener(this);
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     message frags = new message();
     fragmentTransaction.add(R.id.content_main, frags, "text");
     fragmentTransaction.commit();

     check += 1;
 
 public void message(View v) 
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     message sms = new message();


     fragmentTransaction.replace(R.id.content_main, sms, "text");
     fragmentTransaction.addToBackStack(null);
     fragmentTransaction.commit();
 

 public void scan(View v) 
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

     scan scan = new scan();

     fragmentTransaction.replace(R.id.content_main, scan, "scan");
     fragmentTransaction.addToBackStack(null);
     fragmentTransaction.commit();


 
 public void loadooncreate() 
     setContentView(R.layout.activity_main);
     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
     setSupportActionBar(toolbar);

     DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
     ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
         this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
     drawer.setDrawerListener(toggle);
     toggle.syncState();

     NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
     navigationView.setNavigationItemSelectedListener(this);
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     message frags = new message();
     fragmentTransaction.add(R.id.content_main, frags, "text");
     fragmentTransaction.commit();
     check += 1;
 
 @Override
 public boolean onCreateOptionsMenu(Menu menu) 
     // Inflate the menu; this adds items to the action bar if it is present.
     getMenuInflater().inflate(R.menu.main, menu);

     Toolbar toolbar2 = (Toolbar) findViewById(R.id.toolbar1);
     toolbar2.inflateMenu(R.menu.notify);

     toolbar2.getMenu().findItem(R.id.notifications).setIcon(buildCounterDrawable(2, R.drawable.notification));

     toolbar2.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() 

         @Override
         public boolean onMenuItemClick(MenuItem arg0) 
             if (arg0.getItemId() == R.id.notifications) 
                 arg0.setIcon(buildCounterDrawable(0, R.drawable.notification));
                 ((RelativeLayout) findViewById(R.id.content_main)).removeAllViews();
                 FragmentManager fragmentManager = getFragmentManager();
                 FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                 BlankFragment frag = new BlankFragment();
                 fragmentTransaction.add(R.id.content_main, frag, "first");
                 fragmentTransaction.commit();
             
             return false;
         
     );

     check = 1;
     return true;
 

 @Override
 public boolean onKeyDown(int keyCode, KeyEvent event) 
     if (keyCode == KeyEvent.KEYCODE_BACK) 
         //code to reset view
         FragmentManager fragmentManager = getFragmentManager();
         FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

         if (fragmentManager.findFragmentByTag("first") != null) 

             fragmentTransaction.remove(fragmentManager.findFragmentByTag("first"));
             fragmentTransaction.commit();
             loadooncreate();
          else 

             finish();
         

         return true;
     
     return super.onKeyDown(keyCode, event);
 
 private Drawable buildCounterDrawable(int count, int backgroundImageId) 
     LayoutInflater inflater = LayoutInflater.from(this);
     View view = inflater.inflate(R.layout.counter_menuitem_layout, null);
     view.setBackgroundResource(backgroundImageId);

     if (count == 0) 
         View counterTextPanel = view.findViewById(R.id.counterValuePanel);
         counterTextPanel.setVisibility(View.GONE);
      else 
         //            TextView textView = (TextView) view.findViewById(count);
         //            textView.setText("1");
     
     view.measure(
         View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
         View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
     view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

     view.setDrawingCacheEnabled(true);
     view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
     Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
     view.setDrawingCacheEnabled(false);

     return new BitmapDrawable(getResources(), bitmap);
 

 @Override
 public boolean onPrepareOptionsMenu(Menu menu) 
     if (check > 3) 
         Toast.makeText(this.getApplicationContext(), "sdsdsdfdsfsd", Toast.LENGTH_SHORT).show();
         MenuItem item = menu.getItem(R.id.notifications);
         item.setIcon(buildCounterDrawable(2, R.drawable.notification));

     
     //MenuItem item = (MenuItem)findViewById(R.id.notifications);
     //    item.setIcon(buildCounterDrawable(2, R.drawable.notification));

     return true;
 
 @Override
 public boolean onOptionsItemSelected(MenuItem item) 

     if (item.getItemId() == R.id.Home) 
         //close current fragment;
         FragmentManager fragmentManager = getFragmentManager();
         FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

         if (fragmentManager.findFragmentByTag("first") != null)

             fragmentTransaction.remove(fragmentManager.findFragmentByTag("first"));
         fragmentTransaction.commit();
         loadooncreate();

     
     return super.onOptionsItemSelected(item);
 


 @SuppressWarnings("StatementWithEmptyBody")
 @Override
 public boolean onNavigationItemSelected(MenuItem item) 
     // Handle navigation view item clicks here.
     int id = item.getItemId();

     if (id == R.id.nav_camera) 
         // Handle the camera action
      else if (id == R.id.nav_gallery) 

      else if (id == R.id.nav_slideshow) 

      else if (id == R.id.nav_manage) 

      else if (id == R.id.nav_share) 

      else if (id == R.id.nav_send) 

     

     DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
     drawer.closeDrawer(GravityCompat.START);
     return true;
 


 

最后一个堆栈错误

W/MessageQueue: Handler (android.hardware.Camera$EventHandler) 428e2000 sending message to a Handler on a dead thread
                java.lang.RuntimeException: Handler (android.hardware.Camera$EventHandler) 428e2000 sending message to a Handler on a dead thread
                    at android.os.MessageQueue.enqueueMessage(MessageQueue.java:294)
                    at android.os.Handler.sendMessageAtTime(Handler.java:473)
                    at android.os.Handler.sendMessageDelayed(Handler.java:446)
                    at android.os.Handler.sendMessage(Handler.java:383)
                    at android.hardware.Camera.postEventFromNative(Camera.java:852)
                    at dalvik.system.NativeStart.run(Native Method)

【问题讨论】:

为什么要在 detach() 中再次打开相机? .这个问题可能是因为你没有正确释放相机。 如何以及在何处准确释放相机??加上分离没有被调用,因为我正在替换片段。不删除。 另外我正在打开检查相机是否免费?我终于在释放相机但问题是分离在哪里调用?它不叫 sunil。 相机必须在 onPause 中释放并在 onResume 中重新开始。我认为 zXingScannerView 是您的 cameraView,它可以正确处理大多数情况。我不知道您为什么要创建另一个 mCamera 对象。 现在看看我的暂停代码...广告仍然是完全相同的问题:(@sunil Sunny 【参考方案1】:

你可以试试这个,删除onAttachonDetach。还要在 onCreate 中替换这段代码

if (checkCameraHardware(getActivity().getApplicationContext())) 
    Toast.makeText(this.getActivity(), "camera hardware is free", Toast.LENGTH_SHORT).show();
    zXingScannerView = new ZXingScannerView(this.getActivity().getApplicationContext());
    zXingScannerView.setResultHandler(this);
    zXingScannerView.startCamera(0);
 else 

    Toast.makeText(this.getActivity(), "camera hardware is NOT free", Toast.LENGTH_SHORT).show();

有了这个。

 zXingScannerView = new ZXingScannerView(this.getActivity().getApplicationContext());

然后像这样更改 onPause 和 onResume。

  @Override
  public void onPause()
  super.onPause(); 
      if (zXingScannerView != null) 
          zXingScannerView.stopCamera();
          zXingScannerView.stopCameraPreview();
      
  
  @Override
  public void onResume() 
      super.onResume();
      if (checkCameraHardware(getActivity().getApplicationContext())) 
          Toast.makeText(this.getActivity(), "camera hardware is free", Toast.LENGTH_SHORT).show();
          zXingScannerView.setResultHandler(this); // Register ourselves as a 
          zXingScannerView.startCamera(0); // Start camera on resume
      
  

同时删除所有这些对象

  private SurfaceView mySurfaceView;
  private Camera mCamera;
  private CameraPreview mPreview;

不需要它们,它在您的 zXingScannerView 中。

【讨论】:

【参考方案2】:

实际上,当切换到其他片段时,您并没有释放对象,首先释放 onPause() 方法中的所有对象,然后 onResume() 重新连接相机。试试这个:

@Override
public void onResume() 
    super.onResume();
    if (zXingScannerView!= null) 
        zXingScannerView.stopCameraPreview();
        zXingScannerView.resumeCameraPreview(this);
    

【讨论】:

以上是关于在片段之间切换时如何处理相机?的主要内容,如果未能解决你的问题,请参考以下文章

自然模板(例如Thymeleaf)时如何处理代码重复?

在 monorepo 中的多个应用程序之间共享组件时如何处理共享依赖项

iPhone iOS在呈现图表时如何处理Core Data中的时间间隔?

如何处理没有黑屏的android相机打开

执行 Flux.map() 时如何处理错误

使用 ActiveMerchant 时如何处理超时?