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"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
<FrameLayout |
||||||
android:layout_width="match_parent" |
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
android:layout_height="match_parent"> |
android:id="@+id/focusMarkerContainer" |
||||||
|
android:alpha="0" |
||||||
<FrameLayout |
android:layout_width="55dp" |
||||||
android:id="@+id/focusMarkerContainer" |
android:layout_height="55dp"> |
||||||
android:alpha="0" |
<ImageView |
||||||
android:layout_width="55dp" |
android:id="@+id/focusMarkerFill" |
||||||
android:layout_height="55dp"> |
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
<ImageView |
android:src="@drawable/cameraview_focus_marker_fill" /> |
||||||
android:id="@+id/fill" |
<ImageView |
||||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||||
android:layout_height="match_parent" |
android:layout_height="match_parent" |
||||||
android:src="@drawable/focus_marker_fill" /> |
android:src="@drawable/cameraview_focus_marker_outline" /> |
||||||
|
</FrameLayout> |
||||||
<ImageView |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent" |
|
||||||
android:src="@drawable/focus_marker_outline" /> |
|
||||||
|
|
||||||
</FrameLayout> |
|
||||||
|
|
||||||
</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