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