Fun Android Camera development (4): the preview interface is highlighted in the middle of the dark, and only takes pictures of the rectangular area (with the complete source code)

Source: Internet
Author: User

The miscellaneous has previously written a demo about only taking images in a specific area, but it is relatively simple. It is not very rigorous in the conversion of the subject, and the effect of the dark center around the preview interface is not completed, I am sorry, but I have completed this today.

Before proceeding to the code, we should first explain that there are two conversion modes. First, it is converted based on the rectangular area on the screen. For example, a rectangle in the middle of the screen is 100dip * 100dip. dip must be used here. Otherwise, the size of the rectangle is different on different mobile phones. First, convert the dip into px, then calculate the rectangular area based on the width and height of the screen, and pass it to a layer of View on the Surfaceview. Here it is called MaskView, which allows the MaskView to be drawn. Then, when taking a picture, convert the size of the Rectangular Box and the size of the screen to the PictureSize of the final picture to get the image of the rectangular area, and then extract and save it. The second mode is to know the length and width of the desired image in advance. For example, I want to cut an image of 400*400 (unit: px. Use this as a benchmark to calculate the length and width of the Rect displayed on the screen, and then draw the MaskView. Select the mode as needed. This article uses the first mode as an example. The code below:

Package on the basis of the miscellaneous documents. First, encapsulate a MaskView to draw the bright effect in the dark, or you can add a scroll bar. This is not a problem.

I. MaskView. java

Package org. yanzi. ui; import org. yanzi. util. displayUtil; import android. content. context; import android. graphics. canvas; import android. graphics. color; import android. graphics. paint; import android. graphics. paint. style; import android. graphics. point; import android. graphics. rect; import android. util. attributeSet; import android. util. log; import android. widget. imageView; public class MaskView extends ImageView {private static final String TAG = "YanZi"; private Paint mLinePaint; private Paint mAreaPaint; private Rect mCenterRect = null; private Context mContext; public MaskView (Context context, AttributeSet attrs) {super (context, attrs); // TODO Auto-generated constructor stubinitPaint (); mContext = context; Point p = DisplayUtil. getScreenMetrics (mContext); widthScreen = p. x; heightScreen = p. y;} private void initPaint () {// draw PaintmLinePaint = new Paint (Paint. ANTI_ALIAS_FLAG); mLinePaint. setColor (Color. BLUE); mLinePaint. setStyle (Style. STROKE); mLinePaint. setStrokeWidth (5f); mLinePaint. setAlpha (30); // draw the surrounding shadow area mAreaPaint = new Paint (Paint. ANTI_ALIAS_FLAG); mAreaPaint. setColor (Color. GRAY); mAreaPaint. setStyle (Style. FILL); mAreaPaint. setAlpha (180);} public void setCenterRect (Rect r) {Log. I (TAG, "setCenterRect... "); this. mCenterRect = r; postInvalidate ();} public void clearCenterRect (Rect r) {this. mCenterRect = null;} int widthScreen, heightScreen; @ Overrideprotected void onDraw (Canvas canvas) {// TODO Auto-generated method stubLog. I (TAG, "onDraw... "); if (mCenterRect = null) return; // draw the canvas of the surrounding shadow area. drawRect (0, 0, widthScreen, mCenterRect. top, mAreaPaint); canvas. drawRect (0, mCenterRect. bottom + 1, widthScreen, heightScreen, mAreaPaint); canvas. drawRect (0, mCenterRect. top, mCenterRect. left-1, mCenterRect. bottom + 1, mAreaPaint); canvas. drawRect (mCenterRect. right + 1, mCenterRect. top, widthScreen, mCenterRect. bottom + 1, mAreaPaint); // draw the canvas in the transparent area of the target. drawRect (mCenterRect, mLinePaint); super. onDraw (canvas );}}
Description:

1. In order to make this MaskView more adaptive, set the variable mCenterRect in it. The coordinates of this matrix are converted and the screen size is adapted, take the screen width and height in full screen as the coordinate system, and you do not need to change it.

2. Of course, this MaskView is full screen. Here, I will change the layout of PlayCamera_V1.0.0 to the following:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".CameraActivity" >    <FrameLayout        android:layout_width="match_parent"        android:layout_height="match_parent" >        <org.yanzi.camera.preview.CameraSurfaceView            android:id="@+id/camera_surfaceview"            android:layout_width="0dip"            android:layout_height="0dip" />        <org.yanzi.ui.MaskView            android:id="@+id/view_mask"            android:layout_width="match_parent"            android:layout_height="match_parent" />    </FrameLayout>    <ImageButton        android:id="@+id/btn_shutter"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_centerHorizontal="true"        android:layout_marginBottom="10dip"        android:background="@drawable/btn_shutter_background" /></RelativeLayout>
The change is to make the FrameLayout directly full screen. Do not set it to wrap_content. If it is set to wrap, adjust the Surfaceview size in the code, and set MaskView to wrap, it will think that the length and width of the MaskView are also 0. in addition, to make the Framelayout full screen, you can set the Surfaceview margin to adjust the preview layout size when switching between and in the future. Therefore, the preview parent layout FrameLayout must be full screen.

3. Do not make any mistakes in the + 1-1 areas in the code that draws the shadow area. The order is to first draw the shadow of the top, bottom, left, and right areas.

// Draw the canvas in the shadow area. drawRect (0, 0, widthScreen, mCenterRect. top, mAreaPaint); canvas. drawRect (0, mCenterRect. bottom + 1, widthScreen, heightScreen, mAreaPaint); canvas. drawRect (0, mCenterRect. top, mCenterRect. left-1, mCenterRect. bottom + 1, mAreaPaint); canvas. drawRect (mCenterRect. right + 1, mCenterRect. top, widthScreen, mCenterRect. bottom + 1, mAreaPaint );

2. encapsulate two functions in CameraActivity. java:

/** Generate the width and height of the middle rectangle of the image after taking the photo * @ param w the width of the rectangle on the screen, in px * @ param h the height of the rectangle on the screen, unit: px * @ return */private Point createCenterPictureRect (int w, int h) {int wScreen = DisplayUtil. getScreenMetrics (this ). x; int hScreen = DisplayUtil. getScreenMetrics (this ). y; int wSavePicture = CameraInterface. getInstance (). doGetPrictureSize (). y; // because the image is rotated, int hSavePicture = CameraInterface in width and height. getInstance (). doGetPrictureSize (). x ;/ /Because the image is rotated, float wRate = (float) (wSavePicture)/(float) (wScreen); float hRate = (float) (hSavePicture) /(float) (hScreen); float rate = (wRate <= hRate )? WRate: hRate; // You can also calculate int wRectPicture = (int) (w * wRate) according to the minimum ratio; int hRectPicture = (int) (h * hRate ); return new Point (wRectPicture, hRectPicture);}/*** generate the rectangle in the middle of the screen * @ param w the width of the target rectangle, in px * @ param h the height of the target rectangle, unit: px * @ return */private Rect createCenterScreenRect (int w, int h) {int x1 = DisplayUtil. getScreenMetrics (this ). x/2-w/2; int y1 = DisplayUtil. getScreenMetrics (this ). y/2-h/2; int x2 = x1 + w; int y2 = y1 + h; return new Rect (x1, y1, x2, y2 );}
It is a Point consisting of the width and height of the middle rectangle of the image to generate the rectangular area in the middle of the screen. The input parameters of the two functions are the width and height of the rectangle in the center of the screen in pixels. Here is a condition: The rectangle is centered on the screen. Otherwise, the formula should be properly changed..

3. After previewing is enabled, you can draw a MaskView.

@Overridepublic void cameraHasOpened() {// TODO Auto-generated method stubSurfaceHolder holder = surfaceView.getSurfaceHolder();CameraInterface.getInstance().doStartPreview(holder, previewRate);if(maskView != null){Rect screenCenterRect = createCenterScreenRect(DisplayUtil.dip2px(this, DST_CENTER_RECT_WIDTH),DisplayUtil.dip2px(this, DST_CENTER_RECT_HEIGHT));maskView.setCenterRect(screenCenterRect);}}
Note: Because camera. open is stored in a separate thread. After open, callback is made to cameraHasOpened (). Is this function executed in the main thread and subthread? The answer is also in the subthread.That is, the sub-thread callback is still executed in the sub-thread. Therefore, postInvalidate () is used to refresh the set matrix when MaskView is encapsulated.
public void setCenterRect(Rect r){Log.i(TAG, "setCenterRect...");this.mCenterRect = r;postInvalidate();}

4. The last step is to tell the photo callback.

private class BtnListeners implements OnClickListener{@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch(v.getId()){case R.id.btn_shutter:if(rectPictureSize == null){rectPictureSize = createCenterPictureRect(DisplayUtil.dip2px(CameraActivity.this, DST_CENTER_RECT_WIDTH),DisplayUtil.dip2px(CameraActivity.this, DST_CENTER_RECT_HEIGHT));}CameraInterface.getInstance().doTakePicture(rectPictureSize.x, rectPictureSize.y);break;default:break;}}}
The above is the photo listening. In CameraInterface, rewrite a doTakePicture function:

Int DST_RECT_WIDTH, DST_RECT_HEIGHT; public void doTakePicture (int w, int h) {if (isPreviewing & (mCamera! = Null) {Log. I (TAG, "rectangular Photo size: width =" + w + "h =" + h); DST_RECT_WIDTH = w; DST_RECT_HEIGHT = h; mCamera. takePicture (mShutterCallback, null, mrect1_picturecallback );}}
Here we have an mRectJpegPictureCallback, which corresponds to the class:

/*** Rect shooting of the specified region */PictureCallback mrect?picturecallback = new PictureCallback () // callback of jpeg image data, the most important callback is {public void onPictureTaken (byte [] data, Camera camera) {// TODO Auto-generated method stubLog. I (TAG, "mycallback callback: onPictureTaken... "); Bitmap B = null; if (null! = Data) {B = BitmapFactory. decodeByteArray (data, 0, data. length); // data is the byte data, which is parsed into a bitmap mCamera. stopPreview (); isPreviewing = false;} // Save the image to sdcardif (null! = B) {// After FOCUS_MODE_CONTINUOUS_VIDEO is set, myParam. set ("rotation", 90) becomes invalid. // The image cannot be rotated, so Bitmap rotaBitmap = ImageUtil must be rotated here. getRotateBitmap (B, 90.0f); int x = rotaBitmap. getWidth ()/2-DST_RECT_WIDTH/2; int y = rotaBitmap. getHeight ()/2-DST_RECT_HEIGHT/2; Log. I (TAG, "rotaBitmap. getWidth () = "+ rotaBitmap. getWidth () + "rotaBitmap. getHeight () = "+ rotaBitmap. getHeight (); Bitmap rectBitmap = Bitmap. createBitmap (rotaBitmap, x, y, DST_RECT_WIDTH, DST_RECT_HEIGHT); FileUtil. save Bitmap (rectBitmap); if (rotaBitmap. isRecycled () {rotaBitmap. recycle (); rotaBitmap = null;} if (rectBitmap. isRecycled () {rectBitmap. recycle (); rectBitmap = null ;}// preview mCamera again. startPreview (); isPreviewing = true; if (! B. isRecycled () {B. recycle (); B = null ;}}};


Note:

1. To make the captured area exactly the same as that displayed on the screen, the PreviewSize aspect ratio, PictureSize aspect ratio, and screen preview Surfaceview must be in the same aspect ratio,This is a prerequisite. Then, when the rectangular area length and width of the screen are converted into the rectangular area of the image:

/** Generate the width and height of the rectangle in the middle of the image after taking the photo
* @ Param w the width of the rectangle on the screen, in px
* @ Param h the height of the rectangle on the screen, in px
* @ Return
*/
Private Point createCenterPictureRect (int w, int h ){

Int wScreen = DisplayUtil. getScreenMetrics (this). x;
Int hScreen = DisplayUtil. getScreenMetrics (this). y;
Int wSavePicture = CameraInterface. getInstance (). doGetPrictureSize (). y; // because the image is rotated, the width and height are changed here.
Int hSavePicture = CameraInterface. getInstance (). doGetPrictureSize (). x; // because the image is rotated, the width and height are changed here.
Float wRate = (float) (wSavePicture)/(float) (wScreen );
Float hRate = (float) (hSavePicture)/(float) (hScreen );
Float rate = (wRate <= hRate )? WRate: hRate; // It can also be calculated based on the minimum ratio.

Int wRectPicture = (int) (w * wRate );
Int hRectPicture = (int) (h * hRate );
Return new Point (wRectPicture, hRectPicture );

}

In principle, wRate should be equivalent to hRate !!!!!!!!!!

2. I updated getPropPreviewSize and getPropPictureSize in CamParaUtil, which was previously determined by width. Here I changed it to height for determination. Because the read parameter is 800*480 (width * height), the general height is slightly smaller, so it is determined by height. This height is rotated and becomes wide during the final display and storage.

Public Size getPropPictureSize (List <Camera. size> list, float th, int minHeight) {Collections. sort (list, sizeComparator); int I = 0; for (Size s: list) {if (s. height> = minHeight) & Reset rate (s, th) {Log. I (TAG, "PictureSize: w =" + s. width + "h =" + s. height); break;} I ++;} if (I = list. size () {I = 0; // if not found, select the smallest size} return list. get (I );}
Finally, let's take a look at the effect. I set the size of the rectangle displayed on the screen to 200dip * 200dip. The Camera preview parameter is automatically searched based on the ratio of the screen. The height of the preview size is no less than 400, the height of PictureSize is not less than 1300.

// Set PreviewSize and PictureSizeSize pictureSize = CamParaUtil. getInstance (). getPropPictureSize (mParams. getSupportedPictureSizes (), previewRate, 1300); mParams. setPictureSize (pictureSize. width, pictureSize. height); Size previewSize = CamParaUtil. getInstance (). getPropPreviewSize (mParams. getSupportedPreviewSizes (), previewRate, 400); mParams. setPreviewSize (previewSize. width, previewSize. height );

We can see that the image resolution is not changed for simple capturing. Note that the concept of true resolution is not equal to xxx * xxx. The larger the image is, the less unclear it is. A demo of a specific area where the rectangle area can be moved and stretched at any position will be introduced later. ------------------------------- This article is original, reproduced please indicate the author: yanzi1225627 code download link: csdn: http://download.csdn.net/detail/yanzi1225627/7557539





Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.