Getting error while creating custom view for shimmer layout.
Reference: https://github.com/team-supercharge/ShimmerLayout/blob/master/shimmerlayout/src/main/java/io/supercharge/shimmerlayout/ShimmerLayout.java
Error: System.NotSupportedException: Could not activate JNI Handle 0xbeb6ade8 (key_handle 0xbc32e58) of Java type 'md5f5fa8eda1f5ef6033c7dd372483fe62e/ShimmerSplitsLayout' as managed type 'JammberSplits.Droid.CustomControls.ShimmerSplitsLayout'.
internal class ViewTreeObserverListner : Java.Lang.Object, ViewTreeObserver.IOnPreDrawListener
{
ViewTreeObserver _viewTreeObserver;
ShimmerSplitsLayout _shimmerLayout;
public ViewTreeObserverListner(ViewTreeObserver viewTreeObserver, ShimmerSplitsLayout shimmerLayout)
{
_viewTreeObserver = viewTreeObserver;
_shimmerLayout = shimmerLayout;
}
public bool OnPreDraw()
{
_viewTreeObserver.RemoveOnPreDrawListener(this);
//_shimmerLayout.startShimmerAnimation();
return true;
}
}
internal class ShimmerAnimation : Java.Lang.Object, ValueAnimator.IAnimatorUpdateListener
{
int _animationFromX;
public ShimmerAnimation(int animationFromX)
{
_animationFromX = animationFromX;
}
public void OnAnimationUpdate(ValueAnimator animation)
{
ShimmerSplitsLayout.maskOffsetX = _animationFromX + (int)animation.AnimatedValue;
}
}
public class ShimmerSplitsLayout : FrameLayout
{
private static int DEFAULT_ANIMATION_DURATION = 1500;
private static byte DEFAULT_ANGLE = 20;
private static byte MIN_ANGLE_VALUE = Convert.ToByte(-45);
private static byte MAX_ANGLE_VALUE = 45;
private static byte MIN_MASK_WIDTH_VALUE = 0;
private static byte MAX_MASK_WIDTH_VALUE = 1;
private static byte MIN_GRADIENT_CENTER_COLOR_WIDTH_VALUE = 0;
private static byte MAX_GRADIENT_CENTER_COLOR_WIDTH_VALUE = 1;
public static int maskOffsetX;
private Rect maskRect;
private Paint gradientTexturePaint;
private ValueAnimator maskAnimator;
private Bitmap localMaskBitmap;
private Bitmap maskBitmap;
private Canvas canvasForShimmerMask;
private bool isAnimationReversed;
private bool isAnimationStarted;
private bool autoStart;
private int shimmerAnimationDuration;
private int shimmerColor;
private int shimmerAngle;
private float maskWidth;
private float gradientCenterColorWidth;
private ViewTreeObserver.IOnPreDrawListener startAnimationPreDrawListener;
public ShimmerSplitsLayout(Context context) : base(context)
{
}
public ShimmerSplitsLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
Initialize(context, attrs);
}
public ShimmerSplitsLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
Initialize(context, attrs);
}
public ShimmerSplitsLayout(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
Initialize(context, attrs);
}
protected ShimmerSplitsLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
private void Initialize(Context context, IAttributeSet attrs)
{
SetWillNotDraw(false);
TypedArray a = context.Theme.ObtainStyledAttributes(
attrs,
Resource.Styleable.ShimmerLayout,
0, 0);
try
{
shimmerAngle = a.GetInteger(Resource.Styleable.ShimmerLayout_shimmer_angle, DEFAULT_ANGLE);
shimmerAnimationDuration = a.GetInteger(Resource.Styleable.ShimmerLayout_shimmer_animation_duration, DEFAULT_ANIMATION_DURATION);
shimmerColor = a.GetColor(Resource.Styleable.ShimmerLayout_shimmer_color, Resources.GetColor(Resource.Color.shimmer_color));
autoStart = a.GetBoolean(Resource.Styleable.ShimmerLayout_shimmer_auto_start, false);
maskWidth = a.GetFloat(Resource.Styleable.ShimmerLayout_shimmer_mask_width, 0.5F);
gradientCenterColorWidth = a.GetFloat(Resource.Styleable.ShimmerLayout_shimmer_gradient_center_color_width, 0.1F);
isAnimationReversed = a.GetBoolean(Resource.Styleable.ShimmerLayout_shimmer_reverse_animation, false);
}
finally
{
a.Recycle();
}
setMaskWidth(maskWidth);
setGradientCenterColorWidth(gradientCenterColorWidth);
setShimmerAngle(shimmerAngle);
enableForcedSoftwareLayerIfNeeded();
if (autoStart && Visibility == ViewStates.Visible)
{
startShimmerAnimation();
}
}
protected override void OnDetachedFromWindow()
{
resetShimmering();
base.OnDetachedFromWindow();
}
private void resetIfStarted()
{
if (isAnimationStarted)
{
resetShimmering();
startShimmerAnimation();
}
}
protected override void DispatchDraw(Canvas canvas)
{
if (!isAnimationStarted || Width <= 0 || Height <= 0)
{
base.DispatchDraw(canvas);
}
else
{
dispatchDrawShimmer(canvas);
}
}
private void dispatchDrawShimmer(Canvas canvas)
{
localMaskBitmap = getMaskBitmap();
if (localMaskBitmap == null)
{
return;
}
if (canvasForShimmerMask == null)
{
canvasForShimmerMask = new Canvas(localMaskBitmap);
}
canvasForShimmerMask.DrawColor(Color.Transparent, PorterDuff.Mode.Clear);
canvasForShimmerMask.Save();
canvasForShimmerMask.Translate(-maskOffsetX, 0);
base.DispatchDraw(canvasForShimmerMask);
canvasForShimmerMask.Restore();
drawShimmer(canvas);
localMaskBitmap = null;
}
private void resetShimmering()
{
if (maskAnimator != null)
{
maskAnimator.End();
maskAnimator.RemoveAllUpdateListeners();
}
maskAnimator = null;
gradientTexturePaint = null;
isAnimationStarted = false;
releaseBitMaps();
}
private void drawShimmer(Canvas destinationCanvas)
{
createShimmerPaint();
destinationCanvas.Save();
destinationCanvas.Translate(maskOffsetX, 0);
destinationCanvas.DrawRect(maskRect.Left, 0, maskRect.Width(), maskRect.Height(), gradientTexturePaint);
destinationCanvas.Restore();
}
public void startShimmerAnimation()
{
if (isAnimationStarted)
{
return;
}
if (Width == 0)
{
startAnimationPreDrawListener = new ViewTreeObserverListner(ViewTreeObserver, this);
ViewTreeObserver.AddOnPreDrawListener(startAnimationPreDrawListener);
return;
}
Animator animator = getShimmerAnimation();
animator.Start();
isAnimationStarted = true;
}
private void releaseBitMaps()
{
canvasForShimmerMask = null;
if (maskBitmap != null)
{
maskBitmap.Recycle();
maskBitmap = null;
}
}
private Bitmap getMaskBitmap()
{
if (maskBitmap == null)
{
maskBitmap = createBitmap(maskRect.Width(), Height);
}
return maskBitmap;
}
private void createShimmerPaint()
{
if (gradientTexturePaint != null)
{
return;
}
int edgeColor = reduceColorAlphaValueToZero(shimmerColor);
float shimmerLineWidth = Width / 2 * maskWidth;
float yPosition = (0 <= shimmerAngle) ? Height : 0;
LinearGradient gradient = new LinearGradient(
0, yPosition,
(float)Math.Cos(degreesToRadians(shimmerAngle)) * shimmerLineWidth,
yPosition + (float)Math.Sin(degreesToRadians(shimmerAngle)) * shimmerLineWidth,
new int[] { edgeColor, shimmerColor, shimmerColor, edgeColor },
getGradientColorDistribution(),
Shader.TileMode.Clamp);
BitmapShader maskBitmapShader = new BitmapShader(localMaskBitmap, Shader.TileMode.Clamp, Shader.TileMode.Clamp);
ComposeShader composeShader = new ComposeShader(gradient, maskBitmapShader, PorterDuff.Mode.DstIn);
gradientTexturePaint = new Paint();
gradientTexturePaint.AntiAlias = (true);
gradientTexturePaint.Dither = (true);
gradientTexturePaint.FilterBitmap = (true);
gradientTexturePaint.SetShader(composeShader);
}
public void stopShimmerAnimation()
{
if (startAnimationPreDrawListener != null)
{
ViewTreeObserver.RemoveOnPreDrawListener(startAnimationPreDrawListener);
}
resetShimmering();
}
private Animator getShimmerAnimation()
{
if (maskAnimator != null)
{
return maskAnimator;
}
if (maskRect == null)
{
maskRect = calculateBitmapMaskRect();
}
int animationToX = Width;
int animationFromX;
if (Width > maskRect.Width())
{
animationFromX = -animationToX;
}
else
{
animationFromX = -maskRect.Width();
}
int shimmerBitmapWidth = maskRect.Width();
int shimmerAnimationFullLength = animationToX - animationFromX;
maskAnimator = isAnimationReversed ? ValueAnimator.OfInt(shimmerAnimationFullLength, 0)
: ValueAnimator.OfInt(0, shimmerAnimationFullLength);
maskAnimator.SetDuration(shimmerAnimationDuration);
maskAnimator.RepeatCount = (ObjectAnimator.Infinite);
maskAnimator.AddUpdateListener(new ShimmerAnimation(animationFromX));
return maskAnimator;
}
private Bitmap createBitmap(int width, int height)
{
try
{
return Bitmap.CreateBitmap(width, height, Bitmap.Config.Alpha8);
}
catch (OutOfMemoryException e)
{
return null;
}
}
private int getColor(int id)
{
if (Build.VERSION.SdkInt >= Build.VERSION_CODES.M)
{
return Context.GetColor(id);
}
else
{
//noinspection deprecation
return Resources.GetColor(id);
}
}
private int reduceColorAlphaValueToZero(int actualColor)
{
return Color.Argb(0, actualColor, actualColor, actualColor);
}
private Rect calculateBitmapMaskRect()
{
return new Rect(0, 0, calculateMaskWidth(), Height);
}
private int calculateMaskWidth()
{
double shimmerLineBottomWidth = (Width / 2 * maskWidth) / Math.Cos(degreesToRadians(Math.Abs(shimmerAngle)));
double shimmerLineRemainingTopWidth = Height * Math.Tan(degreesToRadians(Math.Abs(shimmerAngle)));
return (int)(shimmerLineBottomWidth + shimmerLineRemainingTopWidth);
}
public double degreesToRadians(double degrees)
{
return (degrees * Math.PI) / 180;
}
private float[] getGradientColorDistribution()
{
float[] colorDistribution = new float[4];
colorDistribution[0] = 0;
colorDistribution[3] = 1;
colorDistribution[1] = 0.5F - gradientCenterColorWidth / 2F;
colorDistribution[2] = 0.5F + gradientCenterColorWidth / 2F;
return colorDistribution;
}
private void enableForcedSoftwareLayerIfNeeded()
{
if (Build.VERSION.SdkInt <= Build.VERSION_CODES.JellyBean)
{
SetLayerType(LayerType.Software, null);
}
}
public void setShimmerColor(int shimmerColor)
{
this.shimmerColor = shimmerColor;
resetIfStarted();
}
public void setShimmerAnimationDuration(int durationMillis)
{
this.shimmerAnimationDuration = durationMillis;
resetIfStarted();
}
public void setAnimationReversed(bool animationReversed)
{
this.isAnimationReversed = animationReversed;
resetIfStarted();
}
private void setShimmerAngle(int shimmerAngle)
{
if (shimmerAngle < MIN_ANGLE_VALUE || MAX_ANGLE_VALUE < shimmerAngle)
{
}
this.shimmerAngle = shimmerAngle;
resetIfStarted();
}
private void setGradientCenterColorWidth(float gradientCenterColorWidth)
{
if (gradientCenterColorWidth <= MIN_GRADIENT_CENTER_COLOR_WIDTH_VALUE
|| MAX_GRADIENT_CENTER_COLOR_WIDTH_VALUE <= gradientCenterColorWidth)
{
}
this.gradientCenterColorWidth = gradientCenterColorWidth;
resetIfStarted();
}
private void setMaskWidth(float maskWidth)
{
if (maskWidth <= MIN_MASK_WIDTH_VALUE || MAX_MASK_WIDTH_VALUE < maskWidth)
{
}
this.maskWidth = maskWidth;
resetIfStarted();
}
}
Check your .axml file. You should use lower case letters for the namespace and camelcase for the class name in the axml file (as shown in https://stackoverflow.com/a/54932494/9335822).
Change your xml file like:
<jammbersplits.droid.customcontrols.ShimmerSplitsLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/shimmerLayout"
... />
Related
I have a horizontal Recyclerview. I want a get click action on the padding of Recyclerview. please check my updated bellow code and error log.
I already check Recyclerview: listen to padding click events but here use Kotlin but I need java code.
My Code:
MyRecyclerView extends RecyclerView {
private boolean isValid;
private int x;
private int y;
private final int delta;
private HashMap _$_findViewCache;
public boolean onTouchEvent(#Nullable MotionEvent e) {
boolean onTouchEvent = super.onTouchEvent(e);
Integer var3 = e != null?Integer.valueOf(e.getAction()):null;
boolean var4 = false;
if(var3 != null) {
if(var3.intValue() == 0) {
this.x = (int)e.getRawX();
this.y = (int)e.getRawY();
this.isValid = true;
return onTouchEvent;
}
}
byte var5 = 2;
if(var3 != null) {
if(var3.intValue() == var5) {
if(Math.abs(e.getRawX() - (float)this.x) > (float)this.delta || Math.abs(e.getRawY() - (float)this.y) > (float)this.delta) {
this.isValid = false;
}
return onTouchEvent;
}
}
var5 = 1;
if(var3 != null) {
if(var3.intValue() == var5 && this.isValid && Math.abs(e.getRawX() - (float)this.x) < (float)this.delta && Math.abs(e.getRawY() - (float)this.y) < (float)this.delta && this.isInRightArea(e)) {
this.performClick();
}
}
return onTouchEvent;
}
private final boolean isInRightArea(MotionEvent e) {
Rect r = new Rect();
this.getGlobalVisibleRect(r);
r.left = this.getPaddingLeft();
r.top += this.getPaddingTop();
return !r.contains((int)e.getRawX(), (int)e.getRawY());
}
#JvmOverloads
public MyRecyclerView(#NotNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
Intrinsics.checkParameterIsNotNull(context, "context");
super(context, attrs, defStyleAttr);
this.delta = ViewConfiguration.get(this.getContext()).getScaledTouchSlop();
}
// $FF: synthetic method
#JvmOverloads
public MyRecyclerView(Context var1, AttributeSet var2, int var3, int var4, DefaultConstructorMarker var5) {
if((var4 & 2) != 0) {
var2 = (AttributeSet)null;
}
if((var4 & 4) != 0) {
var3 = 0;
}
this(var1, var2, var3);
}
#JvmOverloads
public MyRecyclerView(#NotNull Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0, 4, (DefaultConstructorMarker)null);
}
#JvmOverloads
public MyRecyclerView(#NotNull Context context) {
this(context, (AttributeSet)null, 0, 6, (DefaultConstructorMarker)null);
}
public View _$_findCachedViewById(int var1) {
if(this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
if(var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(Integer.valueOf(var1), var2);
}
return var2;
}
public void _$_clearFindViewByIdCache() {
if(this._$_findViewCache != null) {
this._$_findViewCache.clear();
}
}
}
Error log:
This is how you can convert the Kotlin code to Java -
Open Kotlin file in Android Studio
Tools->Kotlin->Show Kotlin Bytecode
hit Decompile button, copy the converted Java code.
Here is the converted Java code.
public final class MyRecyclerView extends RecyclerView {
private boolean isValid;
private int x;
private int y;
private final int delta;
public boolean onTouchEvent(#Nullable MotionEvent e) {
boolean onTouchEvent = super.onTouchEvent(e);
Integer var3 = e != null?Integer.valueOf(e.getAction()):null;
boolean var4 = false;
if(var3 != null) {
if(var3.intValue() == 0) {
this.x = (int)e.getRawX();
this.y = (int)e.getRawY();
this.isValid = true;
return onTouchEvent;
}
}
byte var5 = 2;
if(var3 != null) {
if(var3.intValue() == var5) {
if(Math.abs(e.getRawX() - (float)this.x) > (float)this.delta || Math.abs(e.getRawY() - (float)this.y) > (float)this.delta) {
this.isValid = false;
}
return onTouchEvent;
}
}
var5 = 1;
if(var3 != null) {
if(var3.intValue() == var5 && this.isValid && Math.abs(e.getRawX() - (float)this.x) < (float)this.delta && Math.abs(e.getRawY() - (float)this.y) < (float)this.delta && this.isInRightArea(e)) {
this.performClick();
}
}
return onTouchEvent;
}
private final boolean isInRightArea(MotionEvent e) {
Rect r = new Rect();
this.getGlobalVisibleRect(r);
r.left = this.getPaddingLeft();
r.top += this.getPaddingTop();
return !r.contains((int)e.getRawX(), (int)e.getRawY());
}
#JvmOverloads
public MyRecyclerView(#NotNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
Intrinsics.checkParameterIsNotNull(context, "context");
super(context, attrs, defStyleAttr);
ViewConfiguration var10001 = ViewConfiguration.get(this.getContext());
Intrinsics.checkExpressionValueIsNotNull(var10001, "ViewConfiguration.get(getContext())");
this.delta = var10001.getScaledTouchSlop();
}
// $FF: synthetic method
#JvmOverloads
public MyRecyclerView(Context var1, AttributeSet var2, int var3, int var4, DefaultConstructorMarker var5) {
if((var4 & 2) != 0) {
var2 = (AttributeSet)null;
}
if((var4 & 4) != 0) {
var3 = 0;
}
this(var1, var2, var3);
}
#JvmOverloads
public MyRecyclerView(#NotNull Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0, 4, (DefaultConstructorMarker)null);
}
#JvmOverloads
public MyRecyclerView(#NotNull Context context) {
this(context, (AttributeSet)null, 0, 6, (DefaultConstructorMarker)null);
}
}
Now I'm trying to capture bitmap image from custom class which can detect and track face. But it returns black image. I think the problem is that getDrawingCache() method can't get screenshot image from custom view. Except this custom view, Capturing other views is going well.
Here is the code I used,
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/topLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:orientation="vertical">
<LinearLayout
android:id="#+id/llCameraView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.1">
<com.google.android.gms.samples.vision.face.facetracker.ui.camera.CameraSourcePreview
android:id="#+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.gms.samples.vision.face.facetracker.ui.camera.GraphicOverlay
android:id="#+id/faceOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.gms.samples.vision.face.facetracker.ui.camera.CameraSourcePreview>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="horizontal"
android:background="#00ffffff">
<ImageView
android:id="#+id/ivChangeCamera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/change_64_w"
android:layout_margin="10dp"/>
<ImageView
android:id="#+id/ivTakePicture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/takepic"
android:layout_centerHorizontal="true"
android:layout_margin="10dp"/>
<ImageView
android:id="#+id/ivFilter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/filter_64_w"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_margin="10dp"/>
<ImageView
android:id="#+id/ivSticker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/sticker_64_w"
android:layout_toLeftOf="#id/ivFilter"
android:layout_margin="10dp"/>
</RelativeLayout>
</LinearLayout>
part of MainActivity.java
ImageView takePicture = (ImageView) findViewById(R.id.ivTakePicture);
final LinearLayout llCameraView = (LinearLayout)findViewById(R.id.llCameraView);
takePicture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ContextWrapper wrapper = new ContextWrapper(getApplicationContext());
File file = wrapper.getDir("Images", MODE_PRIVATE);
file = new File(file, "UniqueFileName"+".jpg");
llCameraView.buildDrawingCache();
Bitmap captureView;
llCameraView.setDrawingCacheEnabled(true);
captureView = Bitmap.createBitmap(llCameraView.getDrawingCache());
llCameraView.setDrawingCacheEnabled(false);
try{
OutputStream stream = null;
stream = new FileOutputStream(file);
captureView.compress(Bitmap.CompressFormat.JPEG,100,stream);
stream.flush();
stream.close();
Toast.makeText(getApplicationContext(), "Captured!", Toast.LENGTH_LONG).show();
}catch (IOException e) // Catch the exception
{
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Failed!", Toast.LENGTH_LONG).show();
}
}
});
GraphicOverlay.java
public class GraphicOverlay extends View {
private final Object mLock = new Object();
private int mPreviewWidth;
private float mWidthScaleFactor = 1.0f;
private int mPreviewHeight;
private float mHeightScaleFactor = 1.0f;
private int mFacing = CameraSource.CAMERA_FACING_BACK;
private Set<Graphic> mGraphics = new HashSet<>();
public static abstract class Graphic {
private GraphicOverlay mOverlay;
public Graphic(GraphicOverlay overlay) {
mOverlay = overlay;
}
public abstract void draw(Canvas canvas);
public float scaleX(float horizontal) {
return horizontal * mOverlay.mWidthScaleFactor;
}
public float scaleY(float vertical) {
return vertical * mOverlay.mHeightScaleFactor;
}
public float translateX(float x) {
if (mOverlay.mFacing == CameraSource.CAMERA_FACING_FRONT) {
return mOverlay.getWidth() - scaleX(x);
} else {
return scaleX(x);
}
}
public float translateY(float y) {
return scaleY(y);
}
public void postInvalidate() {
mOverlay.postInvalidate();
}
}
public GraphicOverlay(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void clear() {
synchronized (mLock) {
mGraphics.clear();
}
postInvalidate();
}
public void add(Graphic graphic) {
synchronized (mLock) {
mGraphics.add(graphic);
}
postInvalidate();
}
public void remove(Graphic graphic) {
synchronized (mLock) {
mGraphics.remove(graphic);
}
postInvalidate();
}
public void setCameraInfo(int previewWidth, int previewHeight, int facing) {
synchronized (mLock) {
mPreviewWidth = previewWidth;
mPreviewHeight = previewHeight;
mFacing = facing;
}
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
synchronized (mLock) {
if ((mPreviewWidth != 0) && (mPreviewHeight != 0)) {
mWidthScaleFactor = (float) canvas.getWidth() / (float) mPreviewWidth;
mHeightScaleFactor = (float) canvas.getHeight() / (float) mPreviewHeight;
}
for (Graphic graphic : mGraphics) {
graphic.draw(canvas);
}
}
}
CameraSourcePreview.java
public class CameraSourcePreview extends ViewGroup {
private static final String TAG = "CameraSourcePreview";
private Context mContext;
private SurfaceView mSurfaceView;
private boolean mStartRequested;
private boolean mSurfaceAvailable;
private CameraSource mCameraSource;
private GraphicOverlay mOverlay;
public CameraSourcePreview(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mStartRequested = false;
mSurfaceAvailable = false;
mSurfaceView = new SurfaceView(context);
mSurfaceView.getHolder().addCallback(new SurfaceCallback());
addView(mSurfaceView);
}
public void start(CameraSource cameraSource) throws IOException {
if (cameraSource == null) {
stop();
}
mCameraSource = cameraSource;
if (mCameraSource != null) {
mStartRequested = true;
startIfReady();
}
}
public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException {
mOverlay = overlay;
start(cameraSource);
}
public void stop() {
if (mCameraSource != null) {
mCameraSource.stop();
}
}
public void release() {
if (mCameraSource != null) {
mCameraSource.release();
mCameraSource = null;
}
}
private void startIfReady() throws IOException {
if (mStartRequested && mSurfaceAvailable) {
mCameraSource.start(mSurfaceView.getHolder());
if (mOverlay != null) {
Size size = mCameraSource.getPreviewSize();
int min = Math.min(size.getWidth(), size.getHeight());
int max = Math.max(size.getWidth(), size.getHeight());
if (isPortraitMode()) {
// Swap width and height sizes when in portrait, since it will be rotated by
// 90 degrees
mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing());
} else {
mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing());
}
mOverlay.clear();
}
mStartRequested = false;
}
}
private class SurfaceCallback implements SurfaceHolder.Callback {
#Override
public void surfaceCreated(SurfaceHolder surface) {
mSurfaceAvailable = true;
try {
startIfReady();
} catch (IOException e) {
Log.e(TAG, "Could not start camera source.", e);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surface) {
mSurfaceAvailable = false;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int width = 320;
int height = 240;
if (mCameraSource != null) {
Size size = mCameraSource.getPreviewSize();
if (size != null) {
width = size.getWidth();
height = size.getHeight();
}
}
// Swap width and height sizes when in portrait, since it will be rotated 90 degrees
if (isPortraitMode()) {
int tmp = width;
width = height;
height = tmp;
}
final int layoutWidth = right - left;
final int layoutHeight = bottom - top;
}
for (int i = 0; i < getChildCount(); ++i) {
getChildAt(i).layout(0, 0, layoutWidth, layoutHeight);
}
try {
startIfReady();
} catch (IOException e) {
Log.e(TAG, "Could not start camera source.", e);
}
}
private boolean isPortraitMode() {
int orientation = mContext.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return false;
}
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
return true;
}
Log.d(TAG, "isPortraitMode returning false by default");
return false;
}
}
I am able to capture the image using this:
mCameraSource.takePicture(null, CameraSource.PictureCallback { data ->
file = File.createTempFile("name", ".jpg")
file?.let {
var os: OutputStream? = null
try {
var bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
val bos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.WEBP, 80, bos)
val bitmapData = bos.toByteArray()
val fos = FileOutputStream(file)
fos.write(bitmapData)
fos.flush()
fos.close()
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (os != null) {
try {
os.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
})
Created temp file with the compressed version. You can now play with the temp file.
I am working on Panning and cropping the landscape video using Texture View.I am in a half way that I can pan the landscape video from left to right vice versa by using this example
https://github.com/crust87/Android-VideoCropView.
FFMPEG can crop the particular portion of the video by using this command
ffmpeg -i /sdcard/videokit/in.mp4 -filter:v crop=720:1088:0:0 -c:a
copy /sdcard/videokit/out.mp4
How can I crop only the video which is visible in Texture View and save it local storage in Android.
crop=720:1088:0:0 is a hard coded width and height of the video and it is cropping fine.But how can I get the width and height of the visible video in Texture View to crop the visible video and Save it to the local storage in android.
public class MainActivity extends Activity {
// Layout Components
private FrameLayout top_frame;
// Attributes
private String originalPath;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.check);
top_frame = (FrameLayout)findViewById(R.id.top_frame);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1000 && resultCode == RESULT_OK) {
final VideoCropView mVideoCropView = new VideoCropView(this);
mVideoCropView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mVideoCropView.start();
}
});
top_frame.addView(mVideoCropView);
Uri selectedVideoUri = data.getData();
originalPath = getRealPathFromURI(selectedVideoUri);
mVideoCropView.setVideoURI(selectedVideoUri);
mVideoCropView.seekTo(1);
}
}
public void onButtonLoadClick(View v) {
top_frame.removeAllViews();
Intent lIntent = new Intent(Intent.ACTION_PICK);
lIntent.setType("video/*");
lIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(lIntent, 1000);
}
public String getRealPathFromURI(Uri contentUri) { // getting image path from gallery.
Cursor cursor = null;
try {
String[] proj = { MediaStore.Images.Media.DATA };
cursor = getApplicationContext().getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
CropVideoView
public class VideoCropView extends TextureView implements MediaPlayerControl {
// Constants
private static final String LOG_TAG = "VideoCropView";
private static final int STATE_ERROR = -1;
private static final int STATE_IDLE = 0;
private static final int STATE_PREPARING = 1;
private static final int STATE_PREPARED = 2;
private static final int STATE_PLAYING = 3;
private static final int STATE_PAUSED = 4;
private static final int STATE_PLAYBACK_COMPLETED = 5;
// MediaPlayer Components
protected Context mContext;
private MediaPlayer mMediaPlayer;
private Surface mSurface;
private OnInfoListener mOnInfoListener;
private OnCompletionListener mOCompletionListener;
private OnErrorListener mOnErrorListener;
private OnPreparedListener mOnPreparedListener;
private OnTranslatePositionListener mOnTranslatePositionListener;
// CropView Components
private Matrix mMatrix;
// MediaPlayer Attributes
protected Uri mUri;
private int mCurrentBufferPercentage;
private int mSeekWhenPrepared;
protected int mVideoWidth;
protected int mVideoHeight;
// CropView Attributes
private float mRatioWidth;
private float mRatioHeight;
private float mPositionX;
private float mPositionY;
private float mBoundX;
private float mBoundY;
private int mRotate;
private float mScaleX;
private float mScaleY;
private float mScale;
// Working Variables
private int mCurrentState = STATE_IDLE;
private int mTargetState = STATE_IDLE;
// Touch Event
// past position x, y and move point
float mPastX;
float mPastY;
float mTouchDistance;
private Context context;
// Constructors
public VideoCropView(final Context context) {
super(context);
mContext = context;
initAttributes();
initVideoView();
}
public VideoCropView(final Context context, final AttributeSet attrs) {
super(context, attrs);
mContext = context;
initAttributes(context, attrs, 0);
initVideoView();
}
public VideoCropView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initAttributes(context, attrs, defStyleAttr);
initVideoView();
}
private void initAttributes() {
mRatioWidth = 1;
mRatioHeight = 1;
}
private void initAttributes(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VideoCropView, defStyleAttr, 0);
mRatioWidth = typedArray.getInteger(R.styleable.VideoCropView_ratio_width, 3);
mRatioHeight = typedArray.getInteger(R.styleable.VideoCropView_ratio_height, 4);
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
int heightLayout;
int widthLayout;
widthLayout = MeasureSpec.getSize(widthMeasureSpec);
heightLayout = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthLayout, heightLayout);
/*if(widthMeasureSpec < heightMeasureSpec){
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int) ((width / mRatioWidth) * mRatioHeight);
setMeasuredDimension(width, height);
}else{
int width = MeasureSpec.getSize(widthMeasureSpec);
int height =MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
*/
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(mCurrentState == STATE_ERROR || mCurrentState == STATE_IDLE || mCurrentState == STATE_PREPARING) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPastX = event.getX();
mPastY = event.getY();
mTouchDistance = 0;
case MotionEvent.ACTION_MOVE:
if(mBoundX!=0 || mBoundY!=0) {
float dx = event.getX() - mPastX;
float dy = event.getY() - mPastY;
updateViewPosition(dx, dy);
mPastX = event.getX();
mPastY = event.getY();
mTouchDistance += (Math.abs(dx) + Math.abs(dy));
}
break;
case MotionEvent.ACTION_UP:
if (mTouchDistance < 25) {
if (isPlaying()) {
pause();
} else {
start();
}
}
mTouchDistance = 0;
break;
}
return true;
}
#Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(VideoView.class.getName());
}
#Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(VideoView.class.getName());
}
public int resolveAdjustedSize(int desiredSize, int measureSpec) {
Log.d(LOG_TAG, "Resolve called.");
int result = desiredSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/*
* Parent says we can be as big as we want. Just don't be larger
* than max size imposed on ourselves.
*/
result = desiredSize;
break;
case MeasureSpec.AT_MOST:
/*
* Parent says we can be as big as we want, up to specSize. Don't be
* larger than specSize, and don't be larger than the max size
* imposed on ourselves.
*/
result = Math.min(desiredSize, specSize);
break;
case MeasureSpec.EXACTLY:
// No choice. Do what we are told.
result = specSize;
break;
}
return result;
}
public void initVideoView() {
mVideoHeight = 0;
mVideoWidth = 0;
setFocusable(false);
setSurfaceTextureListener(mSurfaceTextureListener);
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
public void setVideoPath(String path) {
if (path != null) {
setVideoURI(Uri.parse(path));
}
}
public void setVideoURI(Uri pVideoURI) {
mUri = pVideoURI;
mSeekWhenPrepared = 0;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(mContext, pVideoURI);
// create thumbnail bitmap
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
String rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
try {
mRotate = Integer.parseInt(rotation);
} catch(NumberFormatException e) {
mRotate = 0;
}
}
retriever.release();
openVideo();
requestLayout();
invalidate();
}
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
}
public void openVideo() {
if ((mUri == null) || (mSurface == null)) {
// not ready for playback just yet, will try again later
return;
}
// Tell the music playback service to pause
// TODO: these constants need to be published somewhere in the
// framework.
Intent intent = new Intent("com.android.music.musicservicecommand");
intent.putExtra("command", "pause");
mContext.sendBroadcast(intent);
// we shouldn't clear the target state, because somebody might have
// called start() previously
release(false);
try {
mMediaPlayer = new MediaPlayer();
// TODO: create SubtitleController in MediaPlayer, but we need
// a context for the subtitle renderers
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri);
mMediaPlayer.setSurface(mSurface);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
mMediaPlayer.setLooping(true);
mCurrentState = STATE_PREPARING;
} catch (IllegalStateException e) {
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
e.printStackTrace();
} catch (IOException e) {
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
e.printStackTrace();
}
}
private OnVideoSizeChangedListener mSizeChangedListener = new OnVideoSizeChangedListener() {
#Override
public void onVideoSizeChanged(final MediaPlayer mp, final int width,
final int height) {
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
if (mVideoWidth != 0 && mVideoHeight != 0) {
requestLayout();
if(mVideoWidth >= mVideoHeight)
initVideo();
}
}
};
private OnPreparedListener mPreparedListener = new OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mp) {
mCurrentState = STATE_PREPARED;
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mp);
}
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be
// changed after seekTo()
if (seekToPosition != 0) {
seekTo(seekToPosition);
}
if ((mVideoWidth != 0) && (mVideoHeight != 0)) {
if(mVideoWidth >= mVideoHeight) initVideo();
if (mTargetState == STATE_PLAYING) {
start();
}
} else {
// We don't know the video size yet, but should start anyway.
// The video size might be reported to us later.
if (mTargetState == STATE_PLAYING) {
start();
}
}
}
};
private OnCompletionListener mCompletionListener = new OnCompletionListener() {
#Override
public void onCompletion(final MediaPlayer mp) {
mCurrentState = STATE_PLAYBACK_COMPLETED;
mTargetState = STATE_PLAYBACK_COMPLETED;
if (mOCompletionListener != null) {
mOCompletionListener.onCompletion(mMediaPlayer);
}
}
};
private OnInfoListener mInfoListener = new OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
if (mOnInfoListener != null) {
mOnInfoListener.onInfo(mp, arg1, arg2);
}
return true;
}
};
private OnErrorListener mErrorListener = new OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
Log.d(LOG_TAG, "Error: " + framework_err + "," + impl_err);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
/* If an error handler has been supplied, use it and finish. */
if (mOnErrorListener != null) {
if (mOnErrorListener.onError(mMediaPlayer, framework_err,
impl_err)) {
return true;
}
}
return true;
}
};
private OnBufferingUpdateListener mBufferingUpdateListener = new OnBufferingUpdateListener() {
#Override
public void onBufferingUpdate(final MediaPlayer mp, final int percent) {
mCurrentBufferPercentage = percent;
}
};
public void setOnPreparedListener(OnPreparedListener listener) {
mOnPreparedListener = listener;
}
public void setOnCompletionListener(OnCompletionListener listener) {
mOCompletionListener = listener;
}
public void setOnErrorListener(OnErrorListener listener) {
mOnErrorListener = listener;
}
public void setOnInfoListener(OnInfoListener listener) {
mOnInfoListener = listener;
}
private void release(boolean cleartargetstate) {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
mCurrentState = STATE_IDLE;
if (cleartargetstate) {
mTargetState = STATE_IDLE;
}
}
}
#Override
public void start() {
if (isInPlaybackState()) {
mMediaPlayer.start();
mCurrentState = STATE_PLAYING;
}
mTargetState = STATE_PLAYING;
}
#Override
public void pause() {
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
mCurrentState = STATE_PAUSED;
}
}
mTargetState = STATE_PAUSED;
}
#Override
public int getDuration() {
if (isInPlaybackState()) {
return mMediaPlayer.getDuration();
}
return -1;
}
#Override
public int getCurrentPosition() {
if (isInPlaybackState()) {
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
#Override
public void seekTo(int msec) {
if (isInPlaybackState()) {
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
}
}
#Override
public boolean isPlaying() {
return isInPlaybackState() && mMediaPlayer.isPlaying();
}
#Override
public int getBufferPercentage() {
if (mMediaPlayer != null) {
return mCurrentBufferPercentage;
}
return 0;
}
private boolean isInPlaybackState() {
return (mMediaPlayer != null && mCurrentState != STATE_ERROR
&& mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING);
}
#Override
public boolean canPause() {
return false;
}
#Override
public boolean canSeekBackward() {
return false;
}
#Override
public boolean canSeekForward() {
return false;
}
#Override
public int getAudioSessionId() {
return -1;
}
SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
openVideo();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
boolean isValidState = (mTargetState == STATE_PLAYING);
boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height);
if (mMediaPlayer != null && isValidState && hasValidSize) {
start();
}
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
return true;
}
#Override
public void onSurfaceTextureUpdated(final SurfaceTexture surface) {
}
};
#Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == View.INVISIBLE || visibility == View.GONE) {
if (isPlaying()) {
stopPlayback();
}
}
}
public float getScale() {
return mScale;
}
private void initVideo() {
try {
int width = getWidth();
int height = getHeight();
mScaleX = 1.0f;
mScaleY = 1.0f;
mPositionX = 0;
mPositionY = 0;
mBoundX = 0;
mBoundY = 0;
mMatrix = new Matrix();
mScaleX = (float) mVideoWidth / width;
mScaleY = (float) mVideoHeight / height;
mBoundX = width - mVideoWidth / mScaleY;
mBoundY = height - mVideoHeight / mScaleX;
if (mScaleX < mScaleY) {
mScale = mScaleX;
mScaleY = mScaleY * (1.0f / mScaleX);
mScaleX = 1.0f;
mBoundX = 0;
} else {
mScale = mScaleY;
mScaleX = mScaleX * (1.0f / mScaleY);
mScaleY = 1.0f;
mBoundY = 0;
}
mMatrix = new Matrix();
mMatrix.setScale(mScaleX, mScaleY);
setTransform(mMatrix);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
public void updateViewPosition(float x, float y) {
float nextX = mPositionX + x;
float nextY = mPositionY + y;
if(mScaleX == 1.0f) {
x = 0;
} else {
if(nextX > 0) {
x = -mPositionX;
mPositionX = mPositionX + x;
} else if(nextX < mBoundX) {
x = mBoundX - mPositionX;
mPositionX = mPositionX + x;
} else {
mPositionX = nextX;
}
}
if(mScaleY == 1.0f) {
y = 0;
} else {
if(nextY > 0) {
y = -mPositionY;
mPositionY = mPositionY + y;
} else if(nextY < mBoundY) {
y = mBoundY - mPositionY;
mPositionY = mPositionY + y;
} else {
mPositionY = nextY;
}
}
if(mOnTranslatePositionListener != null) {
mOnTranslatePositionListener.onTranslatePosition(mPositionX, mPositionY, mPositionX * -mScale, mPositionY * -mScale);
}
mMatrix.postTranslate(x, y);
setTransform(mMatrix);
invalidate();
}
// public void setOriginalRatio() {
// if(mVideoWidth != 0 && mVideoHeight != 0) {
// int gcd = gcd(mVideoWidth, mVideoHeight);
// setRatio(mVideoWidth / gcd, mVideoHeight / gcd);
// }
// }
public int gcd(int n, int m) {
while (m != 0) {
int t = n % m;
n = m;
m = t;
}
return Math.abs(n);
}
// public void setRatio(float ratioWidth, float ratioHeight) {
// mRatioWidth = ratioWidth;
// mRatioHeight = ratioHeight;
//
// int seek = getCurrentPosition();
//
// requestLayout();
// invalidate();
// openVideo();
//
// seekTo(seek);
// }
public float getRatioWidth() {
return mRatioWidth;
}
public float getRatioHeight() {
return mRatioHeight;
}
public float getRealPositionX() {
return mPositionX * -mScale;
}
public float getRealPositionY() {
return mPositionY * -mScale;
}
public int getVideoWidth() {
return mVideoWidth;
}
public int getVideoHeight() {
return mVideoHeight;
}
public int getRotate() {
return mRotate;
}
public void setOnTranslatePositionListener(OnTranslatePositionListener pOnTranslatePositionListener) {
mOnTranslatePositionListener = pOnTranslatePositionListener;
}
public void setContext(Context context) {
this.context = context;
}
public interface OnTranslatePositionListener {
public abstract void onTranslatePosition(float x, float y, float rx, float ry);
}
}
FFMPEG for cropping particular portion
ffmpeg -i /sdcard/videokit/in.mp4 -filter:v crop=720:1088:0:0 -c:a copy /sdcard/videokit/out.mp4
public class SimpleExample extends Activity {
String workFolder = null;
String demoVideoFolder = null;
String demoVideoPath = null;
String vkLogPath = null;
private boolean commandValidationFailedFlag = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ffmpeg_demo_client_1);
demoVideoFolder = Environment.getExternalStorageDirectory().getAbsolutePath() + "/videokit/";
demoVideoPath = demoVideoFolder + "in.mp4";
Log.i(Prefs.TAG, getString(R.string.app_name) + " version: " + GeneralUtils.getVersionName(getApplicationContext()) );
workFolder = getApplicationContext().getFilesDir().getAbsolutePath() + "/";
//Log.i(Prefs.TAG, "workFolder: " + workFolder);
vkLogPath = workFolder + "vk.log";
GeneralUtils.copyLicenseFromAssetsToSDIfNeeded(this, workFolder);
GeneralUtils.copyDemoVideoFromAssetsToSDIfNeeded(this, demoVideoFolder);
Button invoke = (Button)findViewById(R.id.invokeButton);
invoke.setOnClickListener(new OnClickListener() {
public void onClick(View v){
Log.i(Prefs.TAG, "run clicked.");
if (GeneralUtils.checkIfFileExistAndNotEmpty(demoVideoPath)) {
new TranscdingBackground(SimpleExample.this).execute();
}
else {
Toast.makeText(getApplicationContext(), demoVideoPath + " not found", Toast.LENGTH_LONG).show();
}
}
});
int rc = GeneralUtils.isLicenseValid(getApplicationContext(), workFolder);
Log.i(Prefs.TAG, "License check RC: " + rc);
}
public class TranscdingBackground extends AsyncTask<String, Integer, Integer>
{
ProgressDialog progressDialog;
Activity _act;
String commandStr;
public TranscdingBackground (Activity act) {
_act = act;
}
#Override
protected void onPreExecute() {
EditText commandText = (EditText)findViewById(R.id.CommandText);
commandStr = commandText.getText().toString();
progressDialog = new ProgressDialog(_act);
progressDialog.setMessage("FFmpeg4Android Transcoding in progress...");
progressDialog.show();
}
protected Integer doInBackground(String... paths) {
Log.i(Prefs.TAG, "doInBackground started...");
// delete previous log
boolean isDeleted = GeneralUtils.deleteFileUtil(workFolder + "/vk.log");
Log.i(Prefs.TAG, "vk deleted: " + isDeleted);
PowerManager powerManager = (PowerManager)_act.getSystemService(Activity.POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VK_LOCK");
Log.d(Prefs.TAG, "Acquire wake lock");
wakeLock.acquire();
///////////// Set Command using code (overriding the UI EditText) /////
//commandStr = "ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -s 320x240 -r 30 -aspect 3:4 -ab 48000 -ac 2 -ar 22050 -vcodec mpeg4 -b 2097152 /sdcard/videokit/out.mp4";
//String[] complexCommand = {"ffmpeg", "-y" ,"-i", "/sdcard/videokit/in.mp4","-strict","experimental","-s", "160x120","-r","25", "-vcodec", "mpeg4", "-b", "150k", "-ab","48000", "-ac", "2", "-ar", "22050", "/sdcard/videokit/out.mp4"};
///////////////////////////////////////////////////////////////////////
LoadJNI vk = new LoadJNI();
try {
vk.run(GeneralUtils.utilConvertToComplex(commandStr), workFolder, getApplicationContext());
GeneralUtils.copyFileToFolder(vkLogPath, demoVideoFolder);
} catch (Throwable e) {
Log.e(Prefs.TAG, "vk run exeption.", e);
}
finally {
if (wakeLock.isHeld())
wakeLock.release();
else{
Log.i(Prefs.TAG, "Wake lock is already released, doing nothing");
}
}
Log.i(Prefs.TAG, "doInBackground finished");
return Integer.valueOf(0);
}
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected void onCancelled() {
Log.i(Prefs.TAG, "onCancelled");
//progressDialog.dismiss();
super.onCancelled();
}
#Override
protected void onPostExecute(Integer result) {
Log.i(Prefs.TAG, "onPostExecute");
progressDialog.dismiss();
super.onPostExecute(result);
// finished Toast
String rc = null;
if (commandValidationFailedFlag) {
rc = "Command Vaidation Failed";
}
else {
rc = GeneralUtils.getReturnCodeFromLog(vkLogPath);
}
final String status = rc;
SimpleExample.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(SimpleExample.this, status, Toast.LENGTH_LONG).show();
if (status.equals("Transcoding Status: Failed")) {
Toast.makeText(SimpleExample.this, "Check: " + vkLogPath + " for more information.", Toast.LENGTH_LONG).show();
}
}
});
}
}
}
try to use getBitmap: TextureView.getBitmap for VideoCropView
it returns bitmap wanted resolution.
Then you can crop using Bitmap.createBitmap
like this
resizedbitmap=Bitmap.createBitmap(bmp, 0,0,yourwidth, yourheight);
Use this library to crop video with visible portion
Video Trimmer Library
I am Using SelectableTextview in my e-book app where i want to highlight any text from the book. The text is being highlighted by SelectableTextViewer class, and i am saving the selected text in database. Now i want when the user open the e-book next time the same text should be highlighted as it is. But there is no method of setting selected text. How can i solve this problem. Please help me
SelectableTextviewer.java
public class SelectableTextViewer extends RelativeLayout {
private ImageView imgStartSelect;
public static int mStartSelect = -1;
private ImageView imgEndSelect;
public static int mEndSelect = -1;
private int mImgWidth = 40;
private int mImgHeight = 50;
private TextView textView;
private View mCurrentControlFocused;
public static interface ISelectableTextViewerListener {
public void updateSelection(SelectableTextViewer selectableTextViewer);
public void endSelectingText(SelectableTextViewer selectableTextViewer,
String selectedText);
public void stopSelectingText(
SelectableTextViewer selectableTextViewer, String selectedText);
}
private ISelectableTextViewerListener selectableTextViewerListener;
public static BackgroundColorSpan spanBackgroundColored;
public void setSelectableTextViewerListener(
ISelectableTextViewerListener selectableTextViewerListener) {
this.selectableTextViewerListener = selectableTextViewerListener;
}
public SelectableTextViewer(Context context) {
super(context);
if (!isInEditMode()) {
this.initControls();
}
}
public SelectableTextViewer(Context context, AttributeSet attrs) {
super(context, attrs);
if (!isInEditMode()) {
this.initControls();
}
}
public SelectableTextViewer(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
if (!isInEditMode()) {
this.initControls();
}
}
private void initControls() {
this.spanBackgroundColored = new BackgroundColorSpan(Color.YELLOW);
this.textView = new TextView(getContext());
this.addView(textView);
this.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
showSelectionControls();
int[] location = { 0, 0 };
getLocationOnScreen(location);
System.out.println("getLocationOnScreen:" + location[0] + "\t"
+ location[1]);
return false;
}
});
this.createImgControllersForSelection();
}
protected void disallowIntercept(Boolean disallowIntercept) {
this.getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
}
protected void createImgControllersForSelection() {
this.imgStartSelect = new ImageView(getContext());
this.imgEndSelect = new ImageView(getContext());
this.imgStartSelect.setImageResource(R.drawable.cursor);
this.imgEndSelect.setImageResource(R.drawable.cursor);
this.addView(imgStartSelect, mImgWidth, mImgHeight);
this.addView(imgEndSelect, mImgWidth, mImgHeight);
OnClickListener onClickForChangeFocus = new OnClickListener() {
#Override
public void onClick(View v) {
mCurrentControlFocused = v;
}
};
this.imgEndSelect.setOnClickListener(onClickForChangeFocus);
this.imgStartSelect.setOnClickListener(onClickForChangeFocus);
OnTouchListener onTouchSelectionControl = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
disallowIntercept(true);
mCurrentControlFocused = v;
int eid = event.getAction();
switch (eid) {
case MotionEvent.ACTION_MOVE:
int[] location = { 0, 0 };
getLocationOnScreen(location);
LayoutParams mParams = (LayoutParams) v
.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
// + insideScrollView.getScrollY();
mParams.leftMargin = x - mImgWidth / 2 - location[0];
if (x <= 0) {
mParams.leftMargin = mImgWidth;
} else if (x > (getMeasuredWidth() - mImgWidth)) {
mParams.leftMargin = getMeasuredWidth() - mImgWidth;
}
// TODO Must calculate all padding control
mParams.topMargin = (int) (y - (location[1] + mImgHeight * 1.5f));
if (mParams.topMargin <= 1) {
mParams.topMargin = 1;
}
v.setLayoutParams(mParams);
updateSelectionByMovementImgControls(mParams.leftMargin,
mParams.topMargin);
break;
case MotionEvent.ACTION_UP:
if (selectableTextViewerListener != null) {
selectableTextViewerListener.endSelectingText(
SelectableTextViewer.this, getSelectedText());
}
break;
default:
disallowIntercept(false);
break;
}
return true;
}
};
this.imgEndSelect.setOnTouchListener(onTouchSelectionControl);
this.imgStartSelect.setOnTouchListener(onTouchSelectionControl);
this.imgEndSelect.setVisibility(View.GONE);
this.imgStartSelect.setVisibility(View.GONE);
}
public void updateSelectionByMovementImgControls(int x, int y) {
if (mCurrentControlFocused.equals(imgStartSelect)) {
this.mStartSelect = getOffsetByCoordinates(x + mImgWidth / 2, y);
} else if (mCurrentControlFocused.equals(imgEndSelect)) {
this.mEndSelect = getOffsetByCoordinates(x + mImgWidth / 2, y);
}
updateSelectionSpan();
}
protected Layout updateSelectionSpan() {
Layout retLayout = this.textView.getLayout();
if (this.mStartSelect > -1 && this.mEndSelect > -1) {
if (this.mStartSelect > this.mEndSelect) {
int temp = mEndSelect;
this.mEndSelect = mStartSelect;
this.mStartSelect = temp;
showSelectionControls();
}
SpannedString spannable = (SpannedString) this.textView.getText();
SpannableStringBuilder ssb = new SpannableStringBuilder(spannable);
ssb.removeSpan(this.spanBackgroundColored);
ssb.setSpan(this.spanBackgroundColored, this.mStartSelect,
this.mEndSelect, Spannable.SPAN_USER);
this.textView.setText(ssb);
this.textView.requestLayout();
if (this.selectableTextViewerListener != null) {
this.selectableTextViewerListener.updateSelection(this);
}
}
return retLayout;
}
protected void showSelectionControls() {
if (this.mStartSelect > -1 && this.mEndSelect > -1) {
Layout layout = updateSelectionSpan();
Rect parentTextViewRect = new Rect();
LayoutParams startLP = (LayoutParams) this.imgStartSelect
.getLayoutParams();
float xStart = layout.getPrimaryHorizontal(this.mStartSelect)
- mImgWidth / 2;
float yStart = layout.getLineBounds(
layout.getLineForOffset(this.mStartSelect),
parentTextViewRect);
startLP.setMargins((int) xStart, (int) yStart, -1, -1);
this.imgStartSelect.setLayoutParams(startLP);
this.imgStartSelect.setVisibility(View.VISIBLE);
LayoutParams endLP = (LayoutParams) this.imgEndSelect
.getLayoutParams();
float xEnd = layout.getPrimaryHorizontal(this.mEndSelect)
- mImgWidth / 2;
float yEnd = layout.getLineBounds(
layout.getLineForOffset(this.mEndSelect),
parentTextViewRect);
endLP.setMargins((int) xEnd, (int) yEnd, -1, -1);
this.imgEndSelect.setLayoutParams(endLP);
this.imgEndSelect.setVisibility(View.VISIBLE);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (this.imgStartSelect != null) {
if (this.imgStartSelect.getVisibility() == View.GONE) {
this.onTouchDownCalcSelections(event);
disallowIntercept(false);
} else {
this.stopSelecting();
}
}
} else {
this.disallowIntercept(false);
}
return super.onTouchEvent(event);
}
private void hideSelectionControls() {
this.imgStartSelect.setVisibility(View.GONE);
this.imgEndSelect.setVisibility(View.GONE);
}
private int getOffsetByCoordinates(int x, int y) {
int retOffset = -1;
Layout layout = this.textView.getLayout();
if (layout != null) {
int line = layout.getLineForVertical(y);
retOffset = layout.getOffsetForHorizontal(line, x);
}
return retOffset;
}
private void onTouchDownCalcSelections(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
this.mStartSelect = getOffsetByCoordinates(x, y);
if (this.mStartSelect > -1) {
// Calculate text end
String tempStr = this.textView.getText().toString();
tempStr = tempStr.substring(this.mStartSelect);
Pattern pt = Pattern.compile("\\s");
Matcher mt = pt.matcher(tempStr);
if (mt.find()) {
String match = mt.group(0);
tempStr = tempStr.substring(0, tempStr.indexOf(match));
}
this.mEndSelect = this.mStartSelect + tempStr.length();
}
}
public void setText(SpannableStringBuilder builder) {
this.textView.setText(builder);
}
public ImageView getImgEndSelect() {
return imgEndSelect;
}
public ImageView getImgStartSelect() {
return imgStartSelect;
}
/**
* For this all doing
*
* #return
*/
public String getSelectedText() {
String retSelectedString = null;
if (this.mStartSelect > -1 && this.mEndSelect > -1) {
retSelectedString = this.textView.getText()
.subSequence(this.mStartSelect, this.mEndSelect).toString();
}
return retSelectedString;
}
/**
* Hides cursors and clears
*
* #return
*/
public void stopSelecting() {
this.hideSelectionControls();
SpannedString spannable = (SpannedString) this.textView.getText();
SpannableStringBuilder ssb = new SpannableStringBuilder(spannable);
ssb.removeSpan(this.spanBackgroundColored);
this.setText(ssb);
if (selectableTextViewerListener != null) {
selectableTextViewerListener.stopSelectingText(
SelectableTextViewer.this, getSelectedText());
}
}
}
you have to this method as re-use, customize this method as per your use. this is just a concept.
// text comes from textview.gettext or database text and invalidate the view.
protected Layout updateSelectionSpan(Strint text) {
Layout retLayout = this.textView.getLayout();
if (this.mStartSelect > -1 && this.mEndSelect > -1) {
if (this.mStartSelect > this.mEndSelect) {
int temp = mEndSelect;
this.mEndSelect = mStartSelect;
this.mStartSelect = temp;
showSelectionControls();
}
SpannedString spannable = (SpannedString) text;
SpannableStringBuilder ssb = new SpannableStringBuilder(spannable);
ssb.removeSpan(this.spanBackgroundColored);
ssb.setSpan(this.spanBackgroundColored, this.mStartSelect,
this.mEndSelect, Spannable.SPAN_USER);
this.textView.setText(ssb);
this.textView.requestLayout();
if (this.selectableTextViewerListener != null) {
this.selectableTextViewerListener.updateSelection(this);
}
}
return retLayout;
}
How to change text color of Segmented Controls (if not selected), see below images:
I am getting like this (text in white color):
And want to design this (text in blue color):
Why i am getting space between 3 and 4 button ?
XML:
<com.om.go.widget.SegmentedButton
android:id="#+id/segmented"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
myapp:gradientColorOnStart="#ffffff"
myapp:gradientColorOnEnd="#ffffff"
myapp:gradientColorOffStart="#016de3"
myapp:gradientColorOffEnd="#016de3"
myapp:textStyle="#style/TextViewStyleHeaderButtonBlue"
myapp:strokeColor="#016de3"
myapp:strokeWidth="1dip"
myapp:cornerRadius="4dip"
myapp:btnPaddingTop="7dip"
myapp:btnPaddingBottom="7dip"
/>
SegmentedButton.java:-
public class SegmentedButton extends LinearLayout {
private StateListDrawable mBgLeftOn;
private StateListDrawable mBgRightOn;
private StateListDrawable mBgCenterOn;
private StateListDrawable mBgLeftOff;
private StateListDrawable mBgRightOff;
private StateListDrawable mBgCenterOff;
private int mSelectedButtonIndex = 0;
private List<String> mButtonTitles = new ArrayList<String>();
private int mColorOnStart;
private int mColorOnEnd;
private int mColorOffStart;
private int mColorOffEnd;
private int mColorSelectedStart;
private int mColorSelectedEnd;
private int mColorStroke;
private int mStrokeWidth;
private int mCornerRadius;
private int mTextStyle;
private int mBtnPaddingTop;
private int mBtnPaddingBottom;
private OnClickListenerSegmentedButton mOnClickListenerExternal;
public SegmentedButton(Context context) {
super(context);
}
public SegmentedButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SegmentedButton, 0, 0);
CharSequence btnText1 = a.getString(R.styleable.SegmentedButton_btnText1);
CharSequence btnText2 = a.getString(R.styleable.SegmentedButton_btnText2);
if (btnText1 != null) {
mButtonTitles.add(btnText1.toString());
}
if (btnText2 != null) {
mButtonTitles.add(btnText2.toString());
}
mColorOnStart = a.getColor(R.styleable.SegmentedButton_gradientColorOnStart, 0xFF0000);
mColorOnEnd = a.getColor(R.styleable.SegmentedButton_gradientColorOnEnd, 0xFF0000);
mColorOffStart = a.getColor(R.styleable.SegmentedButton_gradientColorOffStart, 0xFF0000);
mColorOffEnd = a.getColor(R.styleable.SegmentedButton_gradientColorOffEnd, 0xFF0000);
mColorStroke = a.getColor(R.styleable.SegmentedButton_strokeColor, 0xFF0000);
mColorSelectedEnd = a.getColor(R.styleable.SegmentedButton_gradientColorSelectedEnd, 0xFF0000);
mColorSelectedStart = a.getColor(R.styleable.SegmentedButton_gradientColorSelectedStart, 0xFF0000);
mStrokeWidth = a.getDimensionPixelSize(R.styleable.SegmentedButton_strokeWidth, 1);
mCornerRadius = a.getDimensionPixelSize(R.styleable.SegmentedButton_cornerRadius, 4);
mTextStyle = a.getResourceId(R.styleable.SegmentedButton_textStyle, -1);
mBtnPaddingTop = a.getDimensionPixelSize(R.styleable.SegmentedButton_btnPaddingTop, 0);
mBtnPaddingBottom = a.getDimensionPixelSize(R.styleable.SegmentedButton_btnPaddingBottom, 0);
a.recycle();
buildDrawables(mColorOnStart, mColorOnEnd, mColorOffStart, mColorOffEnd,
mColorSelectedStart, mColorSelectedEnd, mCornerRadius, mColorStroke,
mStrokeWidth);
if (mButtonTitles.size() > 0) {
_addButtons(new String[mButtonTitles.size()]);
}
}
public void clearButtons() {
removeAllViews();
}
public void addButtons(String ... titles) {
_addButtons(titles);
}
#SuppressWarnings("deprecation")
private void _addButtons(String[] titles) {
for (int i = 0; i < titles.length; i++) {
Button button = new Button(getContext());
button.setText(titles[i]);
button.setTag(new Integer(i));
button.setOnClickListener(mOnClickListener);
if (mTextStyle != -1) {
button.setTextAppearance(getContext(), mTextStyle);
}
if (titles.length == 1) {
// Don't use a segmented button with one button.
return;
} else if (titles.length == 2) {
if (i == 0) {
button.setBackgroundDrawable(mBgLeftOff);
} else {
button.setBackgroundDrawable(mBgRightOn);
}
} else {
if (i == 0) {
button.setBackgroundDrawable(mBgLeftOff);
} else if (i == titles.length-1) {
button.setBackgroundDrawable(mBgRightOn);
} else {
button.setBackgroundDrawable(mBgCenterOn);
}
}
LinearLayout.LayoutParams llp =
new LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
1);
addView(button, llp);
button.setPadding(0, mBtnPaddingTop, 0, mBtnPaddingBottom);
}
}
private void buildDrawables(int colorOnStart,
int colorOnEnd,
int colorOffStart,
int colorOffEnd,
int colorSelectedStart,
int colorSelectedEnd,
float crad,
int strokeColor,
int strokeWidth)
{
// top-left, top-right, bottom-right, bottom-left
float[] radiiLeft = new float[] {
crad, crad, 0, 0, 0, 0, crad, crad
};
float[] radiiRight = new float[] {
0, 0, crad, crad, crad, crad, 0, 0
};
float[] radiiCenter = new float[] {
0, 0, 0, 0, 0, 0, 0, 0
};
GradientDrawable leftOn = buildGradientDrawable(colorOnStart, colorOnEnd, strokeWidth, strokeColor);
leftOn.setCornerRadii(radiiLeft);
GradientDrawable leftOff = buildGradientDrawable(colorOffStart, colorOffEnd, strokeWidth, strokeColor);
leftOff.setCornerRadii(radiiLeft);
GradientDrawable leftSelected = buildGradientDrawable(colorSelectedStart, colorSelectedEnd, strokeWidth, strokeColor);
leftSelected.setCornerRadii(radiiLeft);
GradientDrawable rightOn = buildGradientDrawable(colorOnStart, colorOnEnd, strokeWidth, strokeColor);
rightOn.setCornerRadii(radiiRight);
GradientDrawable rightOff = buildGradientDrawable(colorOffStart, colorOffEnd, strokeWidth, strokeColor);
rightOff.setCornerRadii(radiiRight);
GradientDrawable rightSelected = buildGradientDrawable(colorSelectedStart, colorSelectedEnd, strokeWidth, strokeColor);
rightSelected.setCornerRadii(radiiRight);
GradientDrawable centerOn = buildGradientDrawable(colorOnStart, colorOnEnd, strokeWidth, strokeColor);
centerOn.setCornerRadii(radiiCenter);
GradientDrawable centerOff = buildGradientDrawable(colorOffStart, colorOffEnd, strokeWidth, strokeColor);
centerOff.setCornerRadii(radiiCenter);
GradientDrawable centerSelected = buildGradientDrawable(colorSelectedStart, colorSelectedEnd, strokeWidth, strokeColor);
centerSelected.setCornerRadii(radiiCenter);
List<int[]> onStates = buildOnStates();
List<int[]> offStates = buildOffStates();
mBgLeftOn = new StateListDrawable();
mBgRightOn = new StateListDrawable();
mBgCenterOn = new StateListDrawable();
mBgLeftOff = new StateListDrawable();
mBgRightOff = new StateListDrawable();
mBgCenterOff = new StateListDrawable();
for (int[] it : onStates) {
mBgLeftOn.addState(it, leftSelected);
mBgRightOn.addState(it, rightSelected);
mBgCenterOn.addState(it, centerSelected);
mBgLeftOff.addState(it, leftSelected);
mBgRightOff.addState(it, rightSelected);
mBgCenterOff.addState(it, centerSelected);
}
for (int[] it : offStates) {
mBgLeftOn.addState(it, leftOn);
mBgRightOn.addState(it, rightOn);
mBgCenterOn.addState(it, centerOn);
mBgLeftOff.addState(it, leftOff);
mBgRightOff.addState(it, rightOff);
mBgCenterOff.addState(it, centerOff);
}
}
private List<int[]> buildOnStates() {
List<int[]> res = new ArrayList<int[]>();
res.add(new int[] {
android.R.attr.state_focused, android.R.attr.state_enabled});
res.add(new int[] {
android.R.attr.state_focused, android.R.attr.state_selected, android.R.attr.state_enabled});
res.add(new int[] {
android.R.attr.state_pressed});
return res;
}
private List<int[]> buildOffStates() {
List<int[]> res = new ArrayList<int[]>();
res.add(new int[] {
android.R.attr.state_enabled});
res.add(new int[] {
android.R.attr.state_selected, android.R.attr.state_enabled});
return res;
}
private GradientDrawable buildGradientDrawable(int colorStart, int colorEnd, int strokeWidth, int strokeColor) {
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[] { colorStart, colorEnd });
gd.setShape(GradientDrawable.RECTANGLE);
gd.setStroke(strokeWidth, strokeColor);
return gd;
}
private OnClickListener mOnClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
Button btnNext = (Button)v;
int btnNextIndex = ((Integer)btnNext.getTag()).intValue();
if (btnNextIndex == mSelectedButtonIndex) {
return;
}
handleStateChange(mSelectedButtonIndex, btnNextIndex);
if (mOnClickListenerExternal != null) {
mOnClickListenerExternal.onClick(mSelectedButtonIndex);
}
}
};
#SuppressWarnings("deprecation")
private void handleStateChange(int btnLastIndex, int btnNextIndex) {
int count = getChildCount();
Button btnLast = (Button)getChildAt(btnLastIndex);
Button btnNext = (Button)getChildAt(btnNextIndex);
if (count < 3) {
if (btnLastIndex == 0) {
btnLast.setBackgroundDrawable(mBgLeftOn);
} else {
btnLast.setBackgroundDrawable(mBgRightOn);
}
if (btnNextIndex == 0) {
btnNext.setBackgroundDrawable(mBgLeftOff);
} else {
btnNext.setBackgroundDrawable(mBgRightOff);
}
} else {
if (btnLastIndex == 0) {
btnLast.setBackgroundDrawable(mBgLeftOn);
} else if (btnLastIndex == count-1) {
btnLast.setBackgroundDrawable(mBgRightOn);
} else {
btnLast.setBackgroundDrawable(mBgCenterOn);
}
if (btnNextIndex == 0) {
btnNext.setBackgroundDrawable(mBgLeftOff);
} else if (btnNextIndex == count-1) {
btnNext.setBackgroundDrawable(mBgRightOff);
} else {
btnNext.setBackgroundDrawable(mBgCenterOff);
}
}
btnLast.setPadding(0, mBtnPaddingTop, 0, mBtnPaddingBottom);
btnNext.setPadding(0, mBtnPaddingTop, 0, mBtnPaddingBottom);
mSelectedButtonIndex = btnNextIndex;
}
public int getSelectedButtonIndex() {
return mSelectedButtonIndex;
}
public void setPushedButtonIndex(int index) {
handleStateChange(mSelectedButtonIndex, index);
}
public void setOnClickListener(OnClickListenerSegmentedButton listener) {
mOnClickListenerExternal = listener;
}
public interface OnClickListenerSegmentedButton {
public void onClick(int index);
}
}