AutoFocusMarker (#484)
	
		
	
				
					
				
			* Create AutoFocusMarker and DefaultAutoFocusMarker * Ensure onFocusEnd is called * Add cameraAutoFocusMarker XML tag * Update docs * Fix changelog and migration guide * Fix testspull/488/head
							parent
							
								
									cd5f0a12bf
								
							
						
					
					
						commit
						ecd2cdba13
					
				| @ -0,0 +1,38 @@ | ||||
| package com.otaliastudios.cameraview.markers; | ||||
| 
 | ||||
| import android.graphics.PointF; | ||||
| 
 | ||||
| import com.otaliastudios.cameraview.CameraView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| /** | ||||
|  * A marker for the autofocus operations. Receives callback when focus starts, | ||||
|  * ends successfully or failed, and can be used to draw on screen. | ||||
|  * | ||||
|  * The point coordinates are meant with respect to {@link CameraView} width and height, | ||||
|  * so a 0, 0 point means that focus is happening on the top-left visible corner. | ||||
|  */ | ||||
| public interface AutoFocusMarker extends Marker { | ||||
| 
 | ||||
|     /** | ||||
|      * Called when the autofocus process has started. | ||||
|      * | ||||
|      * @param trigger the autofocus trigger | ||||
|      * @param point coordinates | ||||
|      */ | ||||
|     void onAutoFocusStart(@NonNull AutoFocusTrigger trigger, @NonNull PointF point); | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Called when the autofocus process has ended, and the camera converged | ||||
|      * to a new focus or failed while trying to do so. | ||||
|      * | ||||
|      * @param trigger the autofocus trigger | ||||
|      * @param successful whether the operation succeeded | ||||
|      * @param point coordinates | ||||
|      */ | ||||
|     void onAutoFocusEnd(@NonNull AutoFocusTrigger trigger, boolean successful, @NonNull PointF point); | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| package com.otaliastudios.cameraview.markers; | ||||
| 
 | ||||
| import com.otaliastudios.cameraview.CameraView; | ||||
| import com.otaliastudios.cameraview.gesture.GestureAction; | ||||
| 
 | ||||
| /** | ||||
|  * Gives information about what triggered the autofocus operation. | ||||
|  */ | ||||
| public enum AutoFocusTrigger { | ||||
| 
 | ||||
|     /** | ||||
|      * Autofocus was triggered by {@link GestureAction#AUTO_FOCUS}. | ||||
|      */ | ||||
|     GESTURE, | ||||
| 
 | ||||
|     /** | ||||
|      * Autofocus was triggered by the {@link CameraView#startAutoFocus(float, float)} method. | ||||
|      */ | ||||
|     METHOD | ||||
| } | ||||
| @ -0,0 +1,79 @@ | ||||
| package com.otaliastudios.cameraview.markers; | ||||
| 
 | ||||
| import android.animation.Animator; | ||||
| import android.animation.AnimatorListenerAdapter; | ||||
| import android.content.Context; | ||||
| import android.graphics.PointF; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import com.otaliastudios.cameraview.R; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * A default implementation of {@link AutoFocusMarker}. | ||||
|  * You can call {@link com.otaliastudios.cameraview.CameraView#setAutoFocusMarker(AutoFocusMarker)} | ||||
|  * passing in this class to have basic marker drawing. | ||||
|  */ | ||||
| public class DefaultAutoFocusMarker implements AutoFocusMarker { | ||||
| 
 | ||||
|     private View mContainer; | ||||
|     private View mFill; | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onAttach(@NonNull Context context, @NonNull ViewGroup container) { | ||||
|         View view = LayoutInflater.from(context).inflate(R.layout.cameraview_layout_focus_marker, container, false); | ||||
|         mContainer = view.findViewById(R.id.focusMarkerContainer); | ||||
|         mFill = view.findViewById(R.id.focusMarkerFill); | ||||
|         return view; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onAutoFocusStart(@NonNull AutoFocusTrigger trigger, @NonNull PointF point) { | ||||
|         if (trigger == AutoFocusTrigger.METHOD) return; | ||||
|         mContainer.clearAnimation(); | ||||
|         mFill.clearAnimation(); | ||||
|         mContainer.setScaleX(1.36f); | ||||
|         mContainer.setScaleY(1.36f); | ||||
|         mContainer.setAlpha(1f); | ||||
|         mFill.setScaleX(0); | ||||
|         mFill.setScaleY(0); | ||||
|         mFill.setAlpha(1f); | ||||
|         animate(mContainer, 1, 1, 300, 0, null); | ||||
|         animate(mFill, 1, 1, 300, 0, null); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onAutoFocusEnd(@NonNull AutoFocusTrigger trigger, boolean successful, @NonNull PointF point) { | ||||
|         if (trigger == AutoFocusTrigger.METHOD) return; | ||||
|         if (successful) { | ||||
|             animate(mContainer, 1, 0, 500, 0, null); | ||||
|             animate(mFill, 1, 0, 500, 0, null); | ||||
|         } else { | ||||
|             animate(mFill, 0, 0, 500, 0, null); | ||||
|             animate(mContainer, 1.36f, 1, 500, 0, new AnimatorListenerAdapter() { | ||||
|                 @Override | ||||
|                 public void onAnimationEnd(Animator animation) { | ||||
|                     super.onAnimationEnd(animation); | ||||
|                     animate(mContainer, 1.36f, 0, 200, 1000, null); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void animate(@NonNull View view, float scale, float alpha, long duration, | ||||
|                                 long delay, @Nullable Animator.AnimatorListener listener) { | ||||
|         view.animate() | ||||
|                 .scaleX(scale) | ||||
|                 .scaleY(scale) | ||||
|                 .alpha(alpha) | ||||
|                 .setDuration(duration) | ||||
|                 .setStartDelay(delay) | ||||
|                 .setListener(listener) | ||||
|                 .start(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package com.otaliastudios.cameraview.markers; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import com.otaliastudios.cameraview.CameraView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * A marker is an overlay over the {@link CameraView} preview, which should be drawn | ||||
|  * at specific times during the camera lifecycle. | ||||
|  * Currently only {@link AutoFocusMarker} is available. | ||||
|  */ | ||||
| public interface Marker { | ||||
| 
 | ||||
|     /** | ||||
|      * Marker is being attached to the CameraView. If a {@link View} is returned, | ||||
|      * it becomes part of the hierarchy and is automatically translated (if possible) | ||||
|      * to match the event place on screen, for example the point where autofocus was started | ||||
|      * by the user finger. | ||||
|      * | ||||
|      * @param context a context | ||||
|      * @param container a container | ||||
|      * @return a view or null | ||||
|      */ | ||||
|     @Nullable | ||||
|     View onAttach(@NonNull Context context, @NonNull ViewGroup container); | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| package com.otaliastudios.cameraview.markers; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.Context; | ||||
| import android.graphics.PointF; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.FrameLayout; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * Manages markers and provides an hierarchy / Canvas for them. | ||||
|  * It is responsible for calling {@link Marker#onAttach(Context, ViewGroup)}. | ||||
|  */ | ||||
| public final class MarkerLayout extends FrameLayout { | ||||
| 
 | ||||
|     public final static int TYPE_AUTOFOCUS = 1; | ||||
| 
 | ||||
|     @SuppressLint("UseSparseArrays") | ||||
|     private final HashMap<Integer, View> mViews = new HashMap<>(); | ||||
| 
 | ||||
|     public MarkerLayout(@NonNull Context context) { | ||||
|         super(context); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Notifies that a new marker was added, possibly replacing another one. | ||||
|      * @param type the marker type | ||||
|      * @param marker the marker | ||||
|      */ | ||||
|     public void onMarker(int type, @Nullable Marker marker) { | ||||
|         // First check if we have a view for a previous marker of this type.
 | ||||
|         View oldView = mViews.get(type); | ||||
|         if (oldView != null) removeView(oldView); | ||||
|         // If new marker is null, we're done.
 | ||||
|         if (marker == null) return; | ||||
|         // Now see if we have a new view.
 | ||||
|         View newView = marker.onAttach(getContext(), this); | ||||
|         if (newView != null) { | ||||
|             mViews.put(type, newView); | ||||
|             addView(newView); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The event that should trigger the drawing is about to be dispatched to | ||||
|      * markers. If we have a valid View, cancel any animations on it and reposition | ||||
|      * it. | ||||
|      * @param type the event type | ||||
|      * @param points the position | ||||
|      */ | ||||
|     public void onEvent(int type, @NonNull PointF[] points) { | ||||
|         View view = mViews.get(type); | ||||
|         if (view == null) return; | ||||
|         view.clearAnimation(); | ||||
|         if (type == TYPE_AUTOFOCUS) { | ||||
|             // TODO can't be sure that getWidth and getHeight are available here.
 | ||||
|             PointF point = points[0]; | ||||
|             float x = (int) (point.x - view.getWidth() / 2); | ||||
|             float y = (int) (point.y - view.getHeight() / 2); | ||||
|             view.setTranslationX(x); | ||||
|             view.setTranslationY(y); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| package com.otaliastudios.cameraview.markers; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| 
 | ||||
| import com.otaliastudios.cameraview.R; | ||||
| import com.otaliastudios.cameraview.controls.Audio; | ||||
| import com.otaliastudios.cameraview.controls.Facing; | ||||
| import com.otaliastudios.cameraview.controls.Flash; | ||||
| import com.otaliastudios.cameraview.controls.Grid; | ||||
| import com.otaliastudios.cameraview.controls.Hdr; | ||||
| import com.otaliastudios.cameraview.controls.Mode; | ||||
| import com.otaliastudios.cameraview.controls.Preview; | ||||
| import com.otaliastudios.cameraview.controls.VideoCodec; | ||||
| import com.otaliastudios.cameraview.controls.WhiteBalance; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * Parses markers from XML attributes. | ||||
|  */ | ||||
| public class MarkerParser { | ||||
| 
 | ||||
|     private AutoFocusMarker autoFocusMarker = null; | ||||
| 
 | ||||
|     public MarkerParser(@NonNull TypedArray array) { | ||||
|         String autoFocusName = array.getString(R.styleable.CameraView_cameraAutoFocusMarker); | ||||
|         if (autoFocusName != null) { | ||||
|             try { | ||||
|                 Class<?> autoFocusClass = Class.forName(autoFocusName); | ||||
|                 autoFocusMarker = (AutoFocusMarker) autoFocusClass.newInstance(); | ||||
|             } catch (Exception ignore) { } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public AutoFocusMarker getAutoFocusMarker() { | ||||
|         return autoFocusMarker; | ||||
|     } | ||||
| } | ||||
| @ -1,25 +1,17 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
| 
 | ||||
|     <FrameLayout | ||||
|         android:id="@+id/focusMarkerContainer" | ||||
|         android:alpha="0" | ||||
|         android:layout_width="55dp" | ||||
|         android:layout_height="55dp"> | ||||
| 
 | ||||
|         <ImageView | ||||
|             android:id="@+id/fill" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:src="@drawable/focus_marker_fill" /> | ||||
| 
 | ||||
|         <ImageView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:src="@drawable/focus_marker_outline" /> | ||||
| 
 | ||||
|     </FrameLayout> | ||||
| 
 | ||||
| </FrameLayout> | ||||
| <FrameLayout | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:id="@+id/focusMarkerContainer" | ||||
|     android:alpha="0" | ||||
|     android:layout_width="55dp" | ||||
|     android:layout_height="55dp"> | ||||
|     <ImageView | ||||
|         android:id="@+id/focusMarkerFill" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:src="@drawable/cameraview_focus_marker_fill" /> | ||||
|     <ImageView | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:src="@drawable/cameraview_focus_marker_outline" /> | ||||
| </FrameLayout> | ||||
|  | ||||
| @ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <string name="cameraview_default_autofocus_marker">com.otaliastudios.cameraview.markers.DefaultAutoFocusMarker</string> | ||||
| </resources> | ||||
					Loading…
					
					
				
		Reference in new issue