I am attempting to make a swipe to unlock feature using a SeekBar. The look I am aiming for is shown here:
This is composed of two images, a background, and a button. I put both the background and the SeekBar in a FrameLayout so that the SeekBar should sit on top of the background.
Like so:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Testing 123..." />
<FrameLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content" >
<ImageView
android:id="#+id/ImageView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="center"
android:src="#drawable/unlockback" />
<SeekBar
android:id="#+id/myseek"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:max="100"
android:progressDrawable="#android:color/transparent"
android:thumb="#drawable/unlockbut" />
</FrameLayout>
</LinearLayout>
Unfortunately the end result looks like this (in eclipse):
I seem to be unable to make the SeekBar match the size of the FrameLayout. You can see the size of the Seekbar represented by a thin blue frame in the image above. The frame has two small solid blue squares which you can grab with the mouse pointer for resizing. But if I use my mouse pointer to drag the little blue square to match the full width of the FrameView, then as soon as I let go of the mouse, the square pings back to its original (too small) size.
What can I do to fix this?.. If I can achieve swipe to unlock in a fundamentally different way, then I'm interested in that too.
As I promised I will see what I can do. I have not used your images and used android graphics to do the drawing as that makes the whole thing more customizable and and scalable. If you do insist in drawing your images, use canvas.drawBitmap... it's pretty simple. The main logic can stay the same.
I may come back and add some fancy animations and visual effects, but for now I left some commented out code to play with shaders and gradients as I am a bit short on time at the moment.
Let's get to it... First crate attrs.xml in /resources/ and add this to it.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SlideToUnlock">
<attr name="sliderColor" format="color"/>
<attr name="cancelOnYExit" format="boolean"/>
<attr name="slideToUnlockText" format="string"/>
<attr name="slideToUnlockTextColor" format="color"/>
<attr name="slideToUnlockBackgroundColor" format="color"/>
<attr name="cornerRadiusX" format="dimension"/>
<attr name="cornerRadiusY" format="dimension"/>
</declare-styleable>
</resources>
And then SlideToUnlock.java
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.EmbossMaskFilter;
import android.graphics.MaskFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Typeface;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by ksenchy on 29.4.2015.
*/
public class SlideToUnlock extends View {
public interface OnSlideToUnlockEventListener {
public void onSlideToUnlockCanceled();
public void onSlideToUnlockDone();
}
private int measuredWidth, measuredHeight;
private float density;
private OnSlideToUnlockEventListener externalListener;
private Paint mBackgroundPaint, mTextPaint, mSliderPaint;
private float rx, ry; // Corner radius
private Path mRoundedRectPath;
private String text = "Unlock →";
float x;
float event_x, event_y;
float radius;
float X_MIN, X_MAX;
private boolean ignoreTouchEvents;
// Do we cancel when the Y coordinate leaves the view?
private boolean cancelOnYExit;
private boolean useDefaultCornerRadiusX, useDefaultCornerRadiusY;
/**
* Default values *
*/
int backgroundColor = 0xFF807B7B;
int textColor = 0xFFFFFFFF;
int sliderColor = 0xAA404040;
public SlideToUnlock(Context context) {
super(context);
init(context, null, 0);
}
public SlideToUnlock(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public SlideToUnlock(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public OnSlideToUnlockEventListener getExternalListener() {
return externalListener;
}
public void setExternalListener(OnSlideToUnlockEventListener externalListener) {
this.externalListener = externalListener;
}
private void init(Context context, AttributeSet attrs, int style) {
Resources res = getResources();
density = res.getDisplayMetrics().density;
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SlideToUnlock, style, 0);
String tmp = a.getString(R.styleable.SlideToUnlock_slideToUnlockText);
text = TextUtils.isEmpty(tmp) ? text : tmp;
rx = a.getDimension(R.styleable.SlideToUnlock_cornerRadiusX, rx);
useDefaultCornerRadiusX = rx == 0;
ry = a.getDimension(R.styleable.SlideToUnlock_cornerRadiusX, ry);
useDefaultCornerRadiusY = ry == 0;
backgroundColor = a.getColor(R.styleable.SlideToUnlock_slideToUnlockBackgroundColor, backgroundColor);
textColor = a.getColor(R.styleable.SlideToUnlock_slideToUnlockTextColor, textColor);
sliderColor = a.getColor(R.styleable.SlideToUnlock_sliderColor, sliderColor);
cancelOnYExit = a.getBoolean(R.styleable.SlideToUnlock_cancelOnYExit, false);
a.recycle();
mRoundedRectPath = new Path();
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundPaint.setStyle(Paint.Style.FILL);
mBackgroundPaint.setColor(backgroundColor);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setColor(textColor);
mTextPaint.setTypeface(Typeface.create("Roboto-Thin", Typeface.NORMAL));
mSliderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mSliderPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSliderPaint.setColor(sliderColor);
mSliderPaint.setStrokeWidth(2 * density);
if (!isInEditMode()) {
// Edit mode does not support shadow layers
// mSliderPaint.setShadowLayer(10.0f, 0.0f, 2.0f, 0xFF000000);
//mSliderPaint.setMaskFilter(new EmbossMaskFilter(new float[]{1, 1, 1}, 0.4f, 10, 8.2f));
float[] direction = new float[]{0.0f, -1.0f, 0.5f};
MaskFilter filter = new EmbossMaskFilter(direction, 0.8f, 15f, 1f);
mSliderPaint.setMaskFilter(filter);
//mSliderPaint.setShader(new LinearGradient(8f, 80f, 30f, 20f, Color.RED,Color.WHITE, Shader.TileMode.MIRROR));
//mSliderPaint.setShader(new RadialGradient(8f, 80f, 90f, Color.RED,Color.WHITE, Shader.TileMode.MIRROR));
//mSliderPaint.setShader(new SweepGradient(80, 80, Color.RED, Color.WHITE));
//mSliderPaint.setMaskFilter(new BlurMaskFilter(15, BlurMaskFilter.Blur.OUTER));
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
if (useDefaultCornerRadiusX) {
rx = measuredHeight * 0.52f;
}
if (useDefaultCornerRadiusY) {
ry = measuredHeight * 0.52f;
}
mTextPaint.setTextSize(measuredHeight / 3.0f);
radius = measuredHeight * 0.45f;
X_MIN = 1.2f * radius;
X_MAX = measuredWidth - X_MIN;
x = X_MIN;
setMeasuredDimension(measuredWidth, measuredHeight);
}
private void drawRoundRect(Canvas c) {
mRoundedRectPath.reset();
mRoundedRectPath.moveTo(rx, 0);
mRoundedRectPath.lineTo(measuredWidth - rx, 0);
mRoundedRectPath.quadTo(measuredWidth, 0, measuredWidth, ry);
mRoundedRectPath.lineTo(measuredWidth, measuredHeight - ry);
mRoundedRectPath.quadTo(measuredWidth, measuredHeight, measuredWidth - rx, measuredHeight);
mRoundedRectPath.lineTo(rx, measuredHeight);
mRoundedRectPath.quadTo(0, measuredHeight, 0, measuredHeight - ry);
mRoundedRectPath.lineTo(0, ry);
mRoundedRectPath.quadTo(0, 0, rx, 0);
c.drawPath(mRoundedRectPath, mBackgroundPaint);
}
#SuppressLint("NewApi")
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (measuredHeight <= 0 || measuredWidth <= 0) {
// There is not much we can draw :/
return;
}
if (Build.VERSION.SDK_INT >= 21) {
canvas.drawRoundRect(0, 0, measuredWidth, measuredHeight, rx, ry, mBackgroundPaint);
}
else {
drawRoundRect(canvas);
}
// Draw the text in center
float xPos = ((measuredWidth - mTextPaint.measureText(text)) / 2.0f);
float yPos = (measuredHeight / 2.0f);
float titleHeight = Math.abs(mTextPaint.descent() + mTextPaint.ascent());
yPos += titleHeight / 2.0f;
canvas.drawText(text, xPos, yPos, mTextPaint);
canvas.drawCircle(x, measuredHeight * 0.5f, radius, mSliderPaint);
}
private void onCancel() {
reset();
if (externalListener != null) {
externalListener.onSlideToUnlockCanceled();
}
}
private void onUnlock() {
if (externalListener != null) {
externalListener.onSlideToUnlockDone();
}
}
private void reset() {
x = X_MIN;
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
ignoreTouchEvents = false;
reset();
return true;
case MotionEvent.ACTION_DOWN:
// Is within the circle??
event_x = event.getX(0);
event_y = event.getY(0);
double squareRadius = radius * radius;
double squaredXDistance = (event_x - X_MIN) * (event_x - X_MIN);
double squaredYDistance = (event_y - measuredHeight / 2) * (event_y - measuredHeight / 2);
if (squaredXDistance + squaredYDistance > squareRadius) {
// User touched outside the button, ignore his touch
ignoreTouchEvents = true;
}
return true;
case MotionEvent.ACTION_CANCEL:
ignoreTouchEvents = true;
onCancel();
case MotionEvent.ACTION_MOVE:
if (!ignoreTouchEvents) {
event_x = event.getX(0);
if (cancelOnYExit) {
event_y = event.getY(0);
if (event_y < 0 || event_y > measuredHeight) {
ignoreTouchEvents = true;
onCancel();
}
}
x = event_x > X_MAX ? X_MAX : event_x < X_MIN ? X_MIN : event_x;
if (event_x >= X_MAX) {
ignoreTouchEvents = true;
onUnlock();
}
invalidate();
}
return true;
default:
return super.onTouchEvent(event);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF000000">
<your.package.SlideToUnlock
android:id="#+id/slideToUnlock"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_centerInParent="true"/>
<your.package.SlideToUnlock
android:id="#+id/slideToUnlock2"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_below="#+id/slideToUnlock"
android:layout_centerInParent="true"
android:layout_marginTop="50dp"
app:cancelOnYExit="true"
app:slideToUnlockBackgroundColor="#808080"
app:slideToUnlockText="Slide to unlock"
app:slideToUnlockTextColor="#03A9F4"
app:sliderColor="#AAFFE97F"/>
</RelativeLayout>
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity implements SlideToUnlock.OnSlideToUnlockEventListener {
private SlideToUnlock slideToUnlockView, slideToUnlockView2;
private Toast toast;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
slideToUnlockView = (SlideToUnlock) findViewById(R.id.slideToUnlock);
slideToUnlockView.setExternalListener(this);
slideToUnlockView2 = (SlideToUnlock) findViewById(R.id.slideToUnlock2);
slideToUnlockView2.setExternalListener(this);
}
private void showToast(String text) {
if (toast != null) {
toast.cancel();
}
toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
toast.show();
}
#Override
public void onSlideToUnlockCanceled() {
showToast("Canceled");
}
#Override
public void onSlideToUnlockDone() {
showToast("Unlocked");
}
}
You can download the whole project here. Enjoy :)
This is the final result.
Here are the libraries that may helps you.
https://github.com/Ghedeon/SlideToUnlockProject
Related
I created a CircularImageView with this question: Create circular image view in android
Download project on GitHub
1) This is the CircularImageView class :
public class CircularImageView extends ImageView {
public CircularImageView(Context context) {
super(context);
}
public CircularImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable)drawable).getBitmap() ;
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
Bitmap roundBitmap = getCroppedBitmap(bitmap, getWidth());
canvas.drawBitmap(roundBitmap, 0, 0, null);
}
public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
Bitmap sbmp;
if(bmp.getWidth() != radius || bmp.getHeight() != radius)
sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
else
sbmp = bmp;
Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(), Bitmap.Config.ARGB_8888);
final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setColor(Color.parseColor("#BAB399"));
Canvas c = new Canvas(output);
c.drawARGB(0, 0, 0, 0);
c.drawCircle(sbmp.getWidth() / 2+0.7f, sbmp.getHeight() / 2+0.7f, sbmp.getWidth() / 2+0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
c.drawBitmap(sbmp, rect, rect, paint);
return output;
}
}
2) I use in my layout like this :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#cccccc"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp" >
<com.mikhaellopez.circularimageview.CircularImageView
android:id="#+id/imageViewCircular"
android:layout_width="#dimen/image_view_size"
android:layout_height="#dimen/image_view_size"
android:layout_gravity="center"
android:background="#drawable/border"
android:src="#drawable/image" />
</LinearLayout>
3) Current result in picture :
How do I change this code to have a shadow and a circular border around my imageView?
Objectif result :
Edit 10/15/2015 :
You can used or download my GitHub library CircularImageView with all the fixes by using gradle dependency :
compile 'com.mikhaellopez:circularimageview:2.0.1'
I modified the CircularImageView found here to achieve what you want.
To create a shadow around the border, I simply used these two lines:
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
You need setLayerType due to hardware acceleration on HoneyComb and up. It didn't work without it when I tried it.
Here is the full code:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class CircularImageView extends ImageView
{
private int borderWidth = 4;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private BitmapShader shader;
public CircularImageView(Context context)
{
super(context);
setup();
}
public CircularImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
setup();
}
public CircularImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setup();
}
private void setup()
{
// init paint
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
setBorderColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
}
public void setBorderWidth(int borderWidth)
{
this.borderWidth = borderWidth;
this.invalidate();
}
public void setBorderColor(int borderColor)
{
if (paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
private void loadBitmap()
{
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if (bitmapDrawable != null)
image = bitmapDrawable.getBitmap();
}
#SuppressLint("DrawAllocation")
#Override
public void onDraw(Canvas canvas)
{
// load the bitmap
loadBitmap();
// init shader
if (image != null)
{
shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
int circleCenter = viewWidth / 2;
// circleCenter is the x or y of the view's center
// radius is the radius in pixels of the cirle to be drawn
// paint contains the shader that will texture the shape
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter + borderWidth - 4.0f, paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter - 4.0f, paint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width - (borderWidth * 2);
viewHeight = height - (borderWidth * 2);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY)
{
// We were told how big to be
result = specSize;
}
else
{
// Measure the text
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY)
{
// We were told how big to be
result = specSize;
}
else
{
// Measure the text (beware: ascent is a negative number)
result = viewHeight;
}
return (result + 2);
}
}
I hope it helps!
.
EDIT
I forked your CircularImageView and added support for selector overlays. I also improved drawing performance significantly...
https://github.com/Pkmmte/CircularImageView
To add a border by making ImageView as a circle, i've done a simple thing, I used this class to make my image as a circle
package com.fidenz.fexceller.fexceller;
/**
* Created by Chathu Hettiarachchi on 5/18/2015.
*/
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
public class RoundedImg extends Drawable {
private final Bitmap mBitmap;
private final Paint mPaint;
private final RectF mRectF;
private final int mBitmapWidth;
private final int mBitmapHeight;
public RoundedImg(Bitmap bitmap) {
mBitmap = bitmap;
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
}
#Override
public void draw(Canvas canvas) {
canvas.drawOval(mRectF, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRectF.set(bounds);
}
#Override
public void setAlpha(int alpha) {
if (mPaint.getAlpha() != alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
#Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
invalidateSelf();
}
#Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
#Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
public Bitmap getBitmap() {
return mBitmap;
}
}
and by using this on onCreate i have call the image for setting it,
profilePic = (ImageView)findViewById(R.id.img_home_profile_pic);
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.no_image);
roundedImage = new RoundedImg(bm);
profilePic.setImageDrawable(roundedImage);
to add a border i created a circle shape XML like this,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
<gradient android:startColor="#color/ring_color" android:endColor="#color/ring_color"
android:angle="270"/>
</shape>
then using layouts i added a RelativeLayout with ImageView inside of it, by using padding and background drawable with wrap_content i set my RelativeLayout like this
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/lay_rel_img"
android:layout_gravity="center"
android:padding="5dp"
android:background="#drawable/circle">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center"
android:id="#+id/img_home_profile_pic"
android:src="#drawable/no_image"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
now it show like this, i don't know to add the shadow, sorry for that too
Add
canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, paint);
before
canvas.drawBitmap(roundBitmap, 0, 0, null);
Change
c.drawCircle(sbmp.getWidth() / 2, sbmp.getHeight() / 2, sbmp.getWidth() / 2, paint);
to
c.drawCircle(sbmp.getWidth() / 2, sbmp.getHeight() / 2, sbmp.getWidth() / 2 - "the border with you prefer", paint);
Hope it helps.
Maybe a better solution here.
Create a custom drawable, and use that to define your the background attribute of your ImageView. You can use a LayeredDrawable, to create as many different components for the component as you would like.
Checkout this answer, which creates a custom Rectangle (but is exactly the same with an Oval\Circle): How to create Google + cards UI in a list view?
I found a library that doing exactly what you wish, worked fine for me.
Check it out.
https://android-arsenal.com/details/1/932
just use drawCircle() method with more width and height before drawing the actual image. Increase the width and height of in that new method call according to your wish, and set some another color of you want on paint
This Class is Custom Circular Imageview with shadow, Stroke,saturation and using this Custom Circular ImageView you can make your image in Circular Shape with Radius. Guys for Circular Shadow ImageView No need Github this class is enough.
Adding CircularImageView to your layout
// Bitmap myimage=BitmapFactory.decodeResource(getResources(),R.drawable.pic);
CircularImageView c=new CircularImageView(this,screen width,screen height,Bitmap myimage);
yourLayout.addView(c);**
public class CircularImageView extends android.support.v7.widget.AppCompatImageView
{
private final Context context;
private final int width, height;
private final Paint paint;
private final Paint paintBorder,imagePaint;
private final Bitmap bitmap2;
private final Paint paint3;
private Bitmap bitmap;
private BitmapShader shader;
private float radius = 4.0f;
float x = 0.0f;
float y = 8.0f;
private float stroke;
private float strokeWidth = 0.0f;
private Bitmap bitmap3;
private int corner_radius=50;
public CircularImageView(Context context, int width, int height, Bitmap bitmap) {
super(context);
this.context = context;
this.width = width;
this.height = height;
//here "bitmap" is the square shape(width* width) scaled bitmap ..
this.bitmap = bitmap;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint3=new Paint();
paint3.setStyle(Paint.Style.STROKE);
paint3.setColor(Color.WHITE);
paint3.setAntiAlias(true);
paintBorder = new Paint();
imagePaint= new Paint();
paintBorder.setColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
this.bitmap2 = Bitmap.createScaledBitmap(bitmap, (bitmap.getWidth() - 40), (bitmap.getHeight() - 40), true);
imagePaint.setAntiAlias(true);
invalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Shader b;
if (bitmap3 != null)
b = new BitmapShader(bitmap3, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
else
b = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
imagePaint.setShader(b);
canvas.drawBitmap(maskedBitmap(), 20, 20, null);
}
private Bitmap maskedBitmap()
{
Bitmap l1 = Bitmap.createBitmap(width,width, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(l1);
paintBorder.setShadowLayer(radius, x, y, Color.parseColor("#454645"));
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
final RectF rect = new RectF();
rect.set(20, 20, bitmap2.getWidth(), bitmap2.getHeight());
canvas.drawRoundRect(rect, corner_radius, corner_radius, paintBorder);
canvas.drawRoundRect(rect, corner_radius, corner_radius, imagePaint);
if (strokeWidth!=0.0f)
{
paint3.setStrokeWidth(strokeWidth);
canvas.drawRoundRect(rect, corner_radius, corner_radius, paint3);
}
paint.setXfermode(null);
return l1;
}
// use seekbar here, here you have to pass "0 -- 250" here corner radius will change
public void setCornerRadius(int corner_radius)
{
this.corner_radius = corner_radius;
invalidate();
}
-------->use seekbar here, here you have to pass "0 -- 10.0f" here shadow radius will change
public void setShadow(float radius)
{
this.radius = radius;
invalidate();
}
// use seekbar here, here you have to pass "0 -- 10.0f" here stroke size will change
public void setStroke(float stroke)
{
this.strokeWidth = stroke;
invalidate();
}
private Bitmap updateSat(Bitmap src, float settingSat)
{
int w = src.getWidth();
int h = src.getHeight();
Bitmap bitmapResult =
Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvasResult = new Canvas(bitmapResult);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(settingSat);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvasResult.drawBitmap(src, 0, 0, paint);
return bitmapResult;
}
// use seekbar here, here you have to pass "0 -- 2.0f" here saturation will change
public void setSaturation(float sat)
{
System.out.println("qqqqqqqqqq "+sat);
bitmap3=updateSat(bitmap2, sat);
invalidate();
}
}
// Seekbar to change radius
radius_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
text_radius.setText(""+progress);
circularImageView.setCornerRadius(progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change shadow
shadow_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
float f= 4+progress/10.0f;
text_shadow.setText(""+progress);
circularImageView.setShadow(f);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change saturation
saturation_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
int progressSat = saturation_seekbar.getProgress();
float sat = (float) ((progressSat*4 / 100.0f)-1.0f);
circularImageView.setSaturation(sat);
text_saturation.setText(""+progressSat);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change stroke
stroke_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if (progress==0)
{
float f=(progress*10.0f/100.0f);
circularImageView.setStroke(f);
}
else
{
float f=(progress*10.0f/100.0f);
circularImageView.setStroke(f);
}
text_stroke.setText(""+progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//radius seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="50"
android:max="250"
android:id="#+id/radius_seekbar"
android:layout_height="wrap_content" />
//saturation seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="50"
android:max="100"
android:id="#+id/saturation_seekbar"
android:layout_height="wrap_content" />
//shadow seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="0"
android:max="100"
android:id="#+id/shadow_seekbar"
android:layout_height="wrap_content" />
//stroke seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="0"
android:max="100"
android:id="#+id/stroke _seekbar"
android:layout_height="wrap_content" />
I find myself in need of assistance.
I'm trying to develop this simple app that takes pictures (Wow, now that's original!). Everything is fine. The only thing I need is to have a CIRCULAR CAMERA PREVIEW.
I have my camerapreview class (which extends surfaceview) placed inside a frame layout, which is my camera preview basically. As you all know, this comes in a rectangular shape. Since I have bigger plans for the app, I'd need the camera preview to be circular (so, for example, someone can take a picture of someone's face and I can have some drawings around...).
Now, I don't know how to proceed. I tried different things, creating a shape with xml and set it as background for my frame layout, but that just didn't work.
After hours spent on google for solutions I decided that I had to give up and come here.
So please, if someone knows anything, let us know :) I hope I was clear enough, do not hesitate to ask for clarification if needed.
Simple way:
1) not setup surface for priview
2) catch raw data
3) convert to bitmap and make circle
4) show (for ex. on imegeview)
just for sample:
public class CameraRoundPriview extends ImageView {
private Camera camera;
private Bitmap bitmap = null;
private int mPreviewWidth, mPreviewHeight;
boolean mCameraOn = false;
public CameraRoundPriview(Context context, AttributeSet attrs) {
super(context, attrs);
}
private Bitmap getclip() {
//clip
Bitmap output = Bitmap.createBitmap(bitmap.getHeight(),
bitmap.getHeight(),
Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getHeight(),
bitmap.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(bitmap.getHeight() / 2,
bitmap.getHeight() / 2,
bitmap.getHeight() / 2, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
//rotate
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(getCameraID(), info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result = degrees;
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP){
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
}
Matrix matrix = new Matrix();
matrix.postRotate(result);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(output,output.getWidth(),output.getHeight(),true);
Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap , 0, 0, scaledBitmap.getWidth(), scaledBitmap .getHeight(), matrix, true);
return rotatedBitmap;
}
private void showImage(){
if ((bitmap != null)){
this.setImageBitmap(getclip());
}
}
public boolean onClickCamera(){
if (mCameraOn){
mCameraOn = false;
cameraStop();
}else{
mCameraOn = true;
cameraStart();
}
return mCameraOn;
}
private void cameraStop(){
if (camera == null) return;
camera.setPreviewCallback(null);
camera.release();
camera = null;
}
private int getCameraID(){
// specify camera id
return 0;
}
private void cameraStart(){
Camera camera = Camera.open(getCameraID());
final Camera.Size previewSize = camera.getParameters().getPreviewSize();
mPreviewWidth = previewSize.width;
mPreviewHeight = previewSize.height;
try {
camera.setPreviewCallback(new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera_call) {
ByteArrayOutputStream outstr = new ByteArrayOutputStream();
Rect rect = new Rect(0, 0, mPreviewWidth, mPreviewHeight);
YuvImage yuvimage=new YuvImage(data,ImageFormat.NV21,mPreviewWidth,mPreviewHeight,null);
yuvimage.compressToJpeg(rect, 50, outstr);
bitmap = BitmapFactory.decodeByteArray(outstr.toByteArray(), 0, outstr.size());
showImage();
camera_call.addCallbackBuffer(data);
}
});
} catch (Exception e) {}
camera.startPreview();
this.camera=camera;
}
}
You can overlay an ImageView over the camera preview. put both the SurfaceView and the ImageView within a FrameLayout both match_parent and the image must be on top.
Set to an black image with transparent circle in the middle.
this is as simple as shown below:
this is for camerax or camera2 API
Note: you must have background transparent.
you must have xml file like below.
<com.RoundedCameraPreview
android:id="#+id/viewFinder"
android:background="#00000000"
app:scaleType="fillCenter"
android:layout_width="match_parent"
android:layout_height="match_parent" />
also don't forget to declare in style.xml
<declare-styleable name="PreviewView">
<attr name="isRound" format="boolean" />
</declare-styleable>
and code is written like this
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.view.PreviewView;
public class RoundedCameraPreview extends PreviewView {
Path clipPath;
boolean isRound;
public RoundedCameraPreview(#NonNull Context context) {
super(context);
}
public RoundedCameraPreview(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public RoundedCameraPreview(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PreviewView,
0, 0);
try {
isRound = a.getBoolean(R.styleable.PreviewView_isRound, true);
} finally {
a.recycle();
}
}
public boolean isRound() {
return isRound;
}
public void setIsRound(boolean isRound) {
this.isRound = isRound;
invalidate();
requestLayout();
}
public RoundedCameraPreview(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void dispatchDraw(Canvas canvas) {
if (isRound) {
clipPath = new Path();
//TODO: define the circle you actually want
clipPath.addCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, canvas.getWidth() / 2, Path.Direction.CW);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.clipPath(clipPath);
canvas.drawPath(clipPath, paint);
}
super.dispatchDraw(canvas);
}
}
Goal
An activity is made to view image, we can pinch zoom or pan the image. The image is centered in the screen in the beginning. Pinch zoom is centered at the center of the image, even after the image is panned somewhere else in the screen.
The image for displaying is downloaded from a given URL, and the URL is passed from extra of an intent to start the image viewing activity.
Pinch zoom is implemented by postScale(), pan by postTranslate().
Problem
After panning the image somewhere, the pinch-zoom center is still at the center of the screen. Tried to follow the center of the image when it's been moved to a new place but my code doesn't work that way. Please give some idea.
The image downloading and panning work well.
Code
activity_image_viewer_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:background="#color/MyPureBlack" >
<LinearLayout
android:id="#+id/progressbar_wrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ProgressBar
android:id="#+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_gravity="center" >
</ProgressBar>
</LinearLayout>
<ImageView
android:id="#+id/image_viewer"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/MyPureBlack"
android:scaleType="matrix" >
</ImageView>
</LinearLayout>
</FrameLayout>
ActivityImageViewer.java
package com.com2us.hubapp.android;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import org.apache.http.util.ByteArrayBuffer;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
public class ActivityImageViewer extends Activity {
File imageFile = null;
// Matrices for pinch zoom and pan
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
Matrix savedMatrixZoom = new Matrix();
// State of motion event
static final int NONE = 0;
static final int PAN = 1;
static final int PINCH_ZOOM = 2;
int mode = NONE;
// The first pointer down
PointF start = new PointF();
// The center of the image (Failed to track it when the image has been moved)
PointF centerOfImage = new PointF();
// oldest is the Cartesian distance between first two pointers when the second pointer is down
float oldDist = 1f;
// MIN_SCALE/MAX_SCALE is the min/max scale factor
private final float MIN_SCALE = 0.5f;
private final float MAX_SCALE = 3.0f;
// TOUCH_SENSITIVE is the minimum Cartesian distance between the first two pointers that triggers the pinch zoom
private final float TOUCH_SENSITIVE = 10.0f;
private final float SPACING_LEFT_AND_RIGHT = 30.0f;
private final float SPACING_TOP_AND_BOTTOM = 30.0f;
// The ImageView widget
private ImageView image_viewer;
// The progress bar shows what current progress is before the image downloading is completed
private ProgressBar progressbar;
private LinearLayout progressbar_wrapper;
// An async task that downloads the image from a given URL
private DownloadFilesTask downloadFilesTask;
private class DownloadFilesTask extends AsyncTask<String, Integer, Bitmap> {
protected Bitmap doInBackground(String... urls) {
InputStream input = null;
OutputStream output = null;
try {
URL url = new URL(urls[0]);
URLConnection connection = url.openConnection();
connection.connect();
int lenghtOfFile = connection.getContentLength();
// download the file
InputStream is = connection.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 8190);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
Bitmap bmp = BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
//final int percent = (int) (total * 100 / lenghtOfFile);
//publishProgress(percent);
//lenghtOfFile
return bmp;
} catch (Exception e) {
} finally {
try {
if (output != null)
output.close();
output = null;
} catch (IOException e) {
}
try {
if (input != null)
input.close();
input = null;
} catch (IOException e) {
}
}
return null;
} // protected Bitmap doInBackground(String... urls) {}
protected void onProgressUpdate(Integer... progress) {
progressbar.setProgress(progress[0]);
}
protected void onPostExecute(Bitmap bmp) {
if (bmp != null) {
final AlphaAnimation animationAfter = new AlphaAnimation(0.0f, 1.0f);
animationAfter.setDuration(300);
animationAfter.setFillEnabled(true);
animationAfter.setFillAfter(true);
image_viewer.setAnimation(animationAfter);
image_viewer.setImageBitmap(bmp);
ViewTreeObserver viewTreeObserver = image_viewer.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Drawable drawable = image_viewer.getDrawable();
int dx = (image_viewer.getWidth() - drawable.getIntrinsicWidth()) / 2;
int dy = (image_viewer.getHeight() - drawable.getIntrinsicHeight()) / 2;
matrix.postTranslate(dx, dy);
image_viewer.setImageMatrix(matrix);
}
});
progressbar_wrapper.setVisibility(View.GONE);
image_viewer.setVisibility(View.VISIBLE);
} else {
android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, 2000);
}
} // End of protected void onPostExecute(Bitmap bmp) {}
} // End of private class DownloadFilesTask extends AsyncTask<String, Integer, Bitmap> {}
// These are activity life cycle handling
// onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
//setTheme(R.style.HubTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_viewer);
progressbar_wrapper = (LinearLayout) findViewById(R.id.progressbar_wrapper);
image_viewer = (ImageView) findViewById(R.id.image_viewer);
progressbar = (ProgressBar) findViewById(R.id.progressbar);
image_viewer.setOnTouchListener(new MyOnTouchListener());
final String uriForImage = getIntent().getStringExtra("url");
downloadFilesTask = new DownloadFilesTask();
downloadFilesTask.execute(uriForImage);
}
// onStart
#Override
protected void onStart() {
super.onStart();
}
// onResume
#Override
protected void onResume() {
super.onResume();
}
// onPause
#Override
protected void onPause() {
super.onPause();
}
// onStop
#Override
protected void onStop() {
super.onStop();
}
// onRestart
#Override
protected void onRestart() {
super.onRestart();
}
// onDestroy
#Override
protected void onDestroy() {
super.onDestroy();
if (imageFile != null) {
try {
Drawable drawable = image_viewer.getDrawable();
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
bitmap.recycle();
drawable = null;
bitmapDrawable = null;
bitmap = null;
} catch (NullPointerException e) {
}
}
}
// onKeyDown
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
this.onBackPressed();
}
return true;
}
// onBackPressed
public void onBackPressed() {
finish();
}
// onConfigurationChanged
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.equals(Configuration.ORIENTATION_LANDSCAPE)) {
} else if (newConfig.equals(Configuration.ORIENTATION_PORTRAIT)) {
}
}
// onLowMemory
#Override
public void onLowMemory() {
super.onLowMemory();
finish();
}
// Get the Cartesian distance between the first two pointers
private float spacing(MotionEvent event) {
float x = 0;
float y = 0;
try {
Method getX = MotionEvent.class.getMethod("getX", Integer.TYPE);
Method getY = MotionEvent.class.getMethod("getX", Integer.TYPE);
// x = event.getX(0) - event.getX(1);
// y = event.getY(0) - event.getY(1);
float x1 = (Float) getX.invoke(event, 0);
float x2 = (Float) getX.invoke(event, 1);
x = x1 - x2;
float y1 = (Float) getY.invoke(event, 0);
float y2 = (Float) getY.invoke(event, 1);
y = y1 - y2;
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
return FloatMath.sqrt(x * x + y * y);
}
// Some flags set manually for convenience
private final int MotionEvent_ACTION_MASK = 255; // that is 0xFF or 11111111
private final int MotionEvent_ACTION_POINTER_DOWN = 5; // that is 101
private final int MotionEvent_ACTION_POINTER_UP = 6; // that is 110
private class MyOnTouchListener implements OnTouchListener {
// onTouch
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
Drawable drawable = view.getDrawable();
if (drawable == null)
return true;
switch (event.getAction() & MotionEvent_ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = PAN;
break;
case MotionEvent_ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > TOUCH_SENSITIVE) {
savedMatrix.set(matrix);
mode = PINCH_ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent_ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == PAN) {
// /////////////////////////////////////////
matrix.set(savedMatrix);
float[] matrixValues = new float[9];
Rect viewRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
matrix.getValues(matrixValues);
float currentY = matrixValues[Matrix.MTRANS_Y];
float currentX = matrixValues[Matrix.MTRANS_X];
float currentScale = matrixValues[Matrix.MSCALE_X];
float currentHeight = drawable.getIntrinsicHeight() * currentScale;
float currentWidth = drawable.getIntrinsicWidth() * currentScale;
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
float newX = currentX + dx;
float newY = currentY + dy;
RectF drawingRect = new RectF(newX, newY, newX + currentWidth, newY + currentHeight);
float diffUp = Math.min(viewRect.bottom - drawingRect.bottom, viewRect.top - drawingRect.top) - SPACING_TOP_AND_BOTTOM;
float diffDown = Math.max(viewRect.bottom - drawingRect.bottom, viewRect.top - drawingRect.top) + SPACING_TOP_AND_BOTTOM;
float diffLeft = Math.min(viewRect.left - drawingRect.left, viewRect.right - drawingRect.right) - SPACING_LEFT_AND_RIGHT;
float diffRight = Math.max(viewRect.left - drawingRect.left, viewRect.right - drawingRect.right) + SPACING_LEFT_AND_RIGHT;
if (diffUp > 0) {
dy += diffUp;
}
if (diffDown < 0) {
dy += diffDown;
}
if (diffLeft > 0) {
dx += diffLeft;
}
if (diffRight < 0) {
dx += diffRight;
}
matrix.postTranslate(dx, dy);
} else if (mode == PINCH_ZOOM) {
float newDist = spacing(event);
if (newDist > TOUCH_SENSITIVE) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
// Get the center of the image. (Failed to get it when image has been moved)
Rect viewRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
centerOfImage.x = viewRect.centerX();
centerOfImage.y = viewRect.centerY();
float[] f = new float[9];
Matrix tmp = new Matrix(matrix);
tmp.postScale(scale, scale, centerOfImage.x, centerOfImage.y);
tmp.getValues(f);
float scaleX = f[Matrix.MSCALE_X];
if (scaleX < MIN_SCALE || scaleX > MAX_SCALE) {
matrix.set(savedMatrixZoom);
} else {
matrix.postScale(scale, scale, centerOfImage.x, centerOfImage.y);
savedMatrixZoom.set(matrix);
}
}
}
break;
}
view.setImageMatrix(matrix);
return true;
} // End of public boolean onTouch(View v, MotionEvent event) {}
} // End of private class MyOnTouchListener implements OnTouchListener {}
} // End of public class ActivityImageViewer extends Activity {}
You can use the Scale Gesture Detector for pinch to zoom. Instead of creating pinch to zoom from scratch you can do something like following,
public class MyCustomView extends View {
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyCustomView(Context mContext){
...
// View code goes here
...
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
...
// onDraw() code goes here
...
canvas.restore();
}
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
Note : Your translation will reside in onDraw method to scale an image.
In my program can I move image across screen? Sometimes I am loosing it from site and cannot find it. I wish to add function which will place imag on original position.
My own aproach wich does not work (and related question to it to make it work):
ImageView iv = current;
Matrix matrix = iv.getImageMatrix;
float[] values = new float[9];
matrix.getValues(values);
int a = values[Matrix.MTRANS_X];
int b = values[Matrix.MTRANS_Y];
matrix.postTranslate(-a,-b);
iv.setImageMatrix(matrix);
EDIT: Latest code
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import ua.mirkvartir.android.frontend.UILApplication;
import android.app.Activity;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.view.MotionEventCompat;
import android.util.FloatMath;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class ImageShowActivity extends Activity {
int p = 0;
Activity app;
ImageAdapterr ia;
ImageView imView;
int imgPos = 0;
Bitmap bm = null;
int geg = 90;
public int width = 0;
public int hight = 0;
Gallery gallery;
Matrix matrix = new Matrix();
Matrix shift = new Matrix();
private int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mLastTouchX = 0;
private float mLastTouchY = 0;
private float mPosX = 0;
private float mPosY = 0;
ImageView current;
public List<ImageView> images;
public int reset = 0;
HashMap<Integer, Bitmap> map = new HashMap<Integer, Bitmap>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_show);
app = this;
images = new ArrayList<ImageView>(
UILApplication.photo_buffer_big.size());
Log.d("images", UILApplication.photo_buffer_big.size() + "");
Log.d("images", images.size() + "");
gallery = (Gallery) findViewById(R.id.gallery);
// EDGES ARE INVISIBLE
gallery.setHorizontalFadingEdgeEnabled(false);
ia = new ImageAdapterr(this);
gallery.setAdapter(ia);
final int length = UILApplication.photo_buffer_big.size();
Button back_btn = (Button) findViewById(R.id.analitics_back_btn);
back_btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
}
});
final TextView img_counter_tv = (TextView) findViewById(R.id.img_counter);
img_counter_tv.setText(p + 1 + "/" + length);
Button nextButton = (Button) findViewById(R.id.next_btn);
nextButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (p < length - 1) {
p++;
} else {
p = 0;
}
gallery.setSelection(p, true);
img_counter_tv.setText(p + 1 + "/" + length);
}
});
Button backButton = (Button) findViewById(R.id.back_btn);
backButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (p == 0) {
p = length - 1;
} else {
p--;
}
gallery.setSelection(p, true);
img_counter_tv.setText(p + 1 + "/" + length);
}
});
}
public void rotateS(View v) {
ImageView iv = current;
Bitmap b = ((BitmapDrawable) iv.getDrawable()).getBitmap();
Matrix matrix = new Matrix();
matrix.postRotate(geg);
Bitmap bMapRotate = Bitmap.createBitmap(b, 0, 0, b.getWidth(),
b.getHeight(), matrix, true);
iv.setImageBitmap(bMapRotate);
}
public void extendS(View v) {
reset = 1;
Log.d("restart", "yes");
runOnUiThread(new Runnable() {
public void run() {
View viewToUpdate = gallery.getChildAt(p - gallery.getFirstVisiblePosition());
viewToUpdate.invalidate();
// ia.notifyDataSetChanged();
}
});
}
public static Bitmap resizeBitmap(Bitmap photo, float x, float y) {
try {
// get current bitmap width and height
int width = photo.getWidth();
int height = photo.getHeight();
// determine how much to scale
float scaleWidth = x / width;
float scaleHeight = y / height;
Log.d("aspect3", "w: " + scaleWidth + " h: " + scaleHeight);
// create the matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bitmap
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new bitmap
Bitmap resizebitmap = Bitmap.createBitmap(photo, 0, 0, width,
height, matrix, false);
return resizebitmap;
} catch (NullPointerException e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
e.printStackTrace();
System.gc();
}
return null;
}
class ImageAdapterr extends BaseAdapter {
/** The parent context */
private Context myContext;
/** Simple Constructor saving the 'parent' context. */
public ImageAdapterr(Context c) {
this.myContext = c;
}
Matrix savedMatrix = new Matrix();
/** Returns the amount of images we have defined. */
public int getCount() {
return UILApplication.photo_buffer_big.size();
}
/* Use the array-Positions as unique IDs */
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
/**
* Returns a new ImageView to be displayed, depending on the position
* passed.
*/
public View getView(final int position, View convertView,
ViewGroup parent) {
ImageView imView = new ImageView(this.myContext);
current = imView;
imgPos = position;
if (bm==null){
AsyncLoad imLoad = new AsyncLoad();
imLoad.execute();
try {
bm = imLoad.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (bm != null) {
imView.setImageBitmap(bm);
} else if (bm == null) {
imView.setImageResource(R.drawable.logo);
}
/* Image should be scaled as width/height are set. */
imView.setScaleType(ImageView.ScaleType.FIT_CENTER);
/* Set the Width/Height of the ImageView. */
imView.setLayoutParams(new Gallery.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
imView.setOnTouchListener(new OnTouchListener() {
private static final String TAG = "Touch";
// These matrices will be used to move and zoom image
PointF start = new PointF();
public PointF mid = new PointF();
// We can be in one of these 3 states
public static final int NONE = 0;
public static final int DRAG = 1;
public static final int ZOOM = 2;
public int mode = NONE;
float oldDist;
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
}
// Perform the transformation
Log.d("point",
(event.getX() - start.x) + " "
+ (event.getY() - start.y));
// Log.d("point",start.x +" "+start.y);
float[] values = new float[9];
matrix.getValues(values);
float a = values[Matrix.MTRANS_X];
float b = values[Matrix.MTRANS_Y];
Log.d("touch matrix", values[Matrix.MPERSP_0] + " "
+ values[Matrix.MPERSP_1] + " "
+ values[Matrix.MPERSP_2]);
Log.d("touch matrix scale", values[Matrix.MSCALE_X] + " "
+ values[Matrix.MSCALE_Y]);
Log.d("touch matrix scew", values[Matrix.MSKEW_X] + " "
+ values[Matrix.MSKEW_Y]);
Log.d("touch matrix trans", values[Matrix.MTRANS_X] + " "
+ values[Matrix.MTRANS_Y]);
if (reset == 1) {
matrix.reset();
savedMatrix.reset();
}
view.setImageMatrix(matrix);
reset = 0;
// images.set(position, view);
return true; // indicate event was handled
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
});
return imView;
}
/**
* Returns the size (0.0f to 1.0f) of the views depending on the
* 'offset' to the center.
*/
public float getScale(boolean focused, int offset) {
/* Formula: 1 / (2 ^ offset) */
return Math.max(0, 1.0f / (float) Math.pow(2, Math.abs(offset)));
}
}
class AsyncLoad extends AsyncTask<Void, Void, Bitmap> {
ProgressDialog pd;
#Override
protected void onPreExecute() {
pd = new ProgressDialog(app);
pd.setOwnerActivity(app);
pd.setTitle("Идет загрузка...");
pd.setCancelable(true);
pd.show();
}
#Override
protected Bitmap doInBackground(Void... arg0) {
// TODO Auto-generated method stub
try {
/*
* Open a new URL and get the InputStream to load data from it.
*/
URL aURL = new URL(UILApplication.photo_buffer_big.get(imgPos));
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
/* Buffered is always good for a performance plus. */
BufferedInputStream bis = new BufferedInputStream(is);
/* Decode url-data to a bitmap. */
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
/* Apply the Bitmap to the ImageView that will be returned. */
// imView.setImageBitmap(bm);
} catch (IOException e) {
// imView.setImageResource(R.drawable.logo);
bm = null;
Log.e("DEBUGTAG", "Remote Image Exception", e);
}
map.put(imgPos, bm);
Log.d("map", map.size() + "");
return bm;
}
#Override
protected void onPostExecute(Bitmap arg0) {
pd.dismiss();
}
}
}
XML
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/top_tab_bg"
android:padding="10dp" >
<Button
android:id="#+id/analitics_back_btn"
style="#style/ButtonText"
android:layout_marginRight="5dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/back_btn"
android:background="#drawable/btn_clk_selector"
android:text="Назад" />
<Button
android:id="#+id/btn_rotate"
style="#style/ButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dip"
android:layout_toLeftOf="#+id/analitics_back_btn"
android:background="#drawable/btn_clk_selector"
android:onClick="rotateS"
android:text="Повернуть" />
<Button
android:id="#+id/btn_ex"
style="#style/ButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dip"
android:layout_toLeftOf="#+id/btn_rotate"
android:background="#drawable/btn_clk_selector"
android:onClick="extendS"
android:text="рас" />
<Button
android:id="#+id/next_btn"
style="#style/ButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="5dp"
android:background="#drawable/next_img_btn"
android:paddingRight="10dp" />
<TextView
android:id="#+id/img_counter"
style="#style/ButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/next_btn"
android:text="1/10" />
<Button
android:id="#+id/back_btn"
style="#style/ButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_toLeftOf="#+id/img_counter"
android:background="#drawable/back_img_btn" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/gal"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Gallery
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/gallery"
android:adjustViewBounds="true"
android:spacing="10dp"
/>
</RelativeLayout>
</LinearLayout>
EDIT: problem with new code - image is not rested after enlargeS was called. It is reseted after enlargeS call + tab on screen. Also image is partially reseted - it goes to original position and scale, but it goes to its original resolution. So usually i bacamos much smaller then screen, while i need it to fill screen.
Use the code here below:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
imageView = new ImageView(this);
imageView.setLayoutParams(params);
imageView.setImageBitmap(bitmap);
layout.setGravity(Gravity.CENTER_VERTICAL | Gravity.TOP);
layout.addView(imageView);
This will position the ImageView at the center of the RelativeLayout which contains it.
EDIT: see in the code above - I changed the RelativeLayout.LayoutParams to LinearLayout.LayoutParams, because it is the LinearLayout, which contains the RelativeLayout in fact. Now it should work.
Try to use this in your code to set the image view wherever you want on the screen,
iconSMS.setImageResource(R.drawable.ic_launcher_smsmms);
LayoutParams paramsIconSMS = new LayoutParams(iconwidth,iconheight);
iconwidthspacing=(int)Math.round(((float)width/480)*(float)45);
iconheightspacing=(int)Math.round(((float)height/800)*(float)520);
paramsIconSMS.setMargins(iconwidthspacing, iconheightspacing, 0, 0);
iconSMS.setId(204);
iconSMS.setLayoutParams(paramsIconSMS);
Here,
iconwidth=(int)Math.round(width*iconwidthf);
iconheight=(int)Math.round(height*iconheightf);
and width and height variables are,
requestWindowFeature(Window.FEATURE_NO_TITLE);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
width = metrics.widthPixels;
height = metrics.heightPixels;
by width and height you are getting the screen size of your phone in pixels.
In my app I have a basic compass which is rendered in a "compassView" Class. If I set the contentView in my Display Compass activity like so: compassView = new CompassView(this); setContentView(compassView); it works fine and points north however if I set the content view to setContentView(activity_display_compass) it will render the compass and any textviews I may have but the compass is static; ie the needle does not move. I am at loss to what is causing this. Any guidance would be much appreciated?
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"/>
<com.example.gpsfinder.CompassView
android:id="#+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Display Compass Activity
public class DisplayCompass extends Activity {
private static SensorManager sensorService;
private CompassView compassView;
private Sensor sensorAccelerometer;
private Sensor sensorMagnetometer;
private float [] lastAccelerometer = new float[3];
private float [] lastMagnetometer = new float[3];
private boolean lastAccelerometerSet = false;
private boolean lastMagnetometerSet = false;
private float[] rotation = new float[9];
private float[] orientation = new float[3];
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
compassView = new CompassView(this);
setContentView(compassView);
sensorService = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensorAccelerometer = sensorService.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMagnetometer = sensorService.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
//sensor = sensorService.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (sensorAccelerometer != null && sensorMagnetometer != null) {
sensorService.registerListener(mySensorEventListener, sensorAccelerometer,
SensorManager.SENSOR_DELAY_UI);
sensorService.registerListener(mySensorEventListener, sensorMagnetometer,
SensorManager.SENSOR_DELAY_UI);
Log.i("Compass MainActivity", "Registerered for ORIENTATION Sensor");
} else {
Log.e("Compass MainActivity", "Registerered for ORIENTATION Sensor");
Toast.makeText(this, "ORIENTATION Sensor not found",
Toast.LENGTH_LONG).show();
finish();
}
}
private SensorEventListener mySensorEventListener = new SensorEventListener() {
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void onSensorChanged(SensorEvent event) {
if(event.sensor == sensorAccelerometer){
System.arraycopy(event.values,0,lastAccelerometer, 0,event.values.length);
lastAccelerometerSet= true;
}
else if (event.sensor == sensorMagnetometer){
System.arraycopy(event.values,0,lastMagnetometer, 0,event.values.length);
lastMagnetometerSet = true;
}
if(lastAccelerometerSet && lastMagnetometerSet)
SensorManager.getRotationMatrix(rotation,null,lastAccelerometer ,lastMagnetometer );
SensorManager.getOrientation(rotation,orientation);
// angle between the magnetic north direction
// 0=North, 90=East, 180=South, 270=West
double azimuth = Math.toDegrees(orientation[0]);
compassView.updateDirection(azimuth);
}
};
#Override
protected void onDestroy() {
super.onDestroy();
sensorService.unregisterListener(mySensorEventListener);
}
}
Compass View
public class CompassView extends View{
private Paint paint;
private double position = 0;
public CompassView(Context context) {
super(context, null);
init();
}
public CompassView(Context context, AttributeSet attrs) {
super(context,attrs, 0);
init();
}
public CompassView(Context context, AttributeSet attrs,int defStyle) {
super(context,attrs, defStyle);
init();
}
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setTextSize(25);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
}
#Override
protected void onDraw(Canvas canvas) {
int xPoint = getMeasuredWidth() / 2;
int yPoint = getMeasuredHeight() / 2;
float radius = (float) (Math.max(xPoint, yPoint) * 0.6);
canvas.drawCircle(xPoint, yPoint, radius, paint);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
// 3.143 is a good approximation for the circle
canvas.drawLine(xPoint,
yPoint,
(float) (xPoint + radius
* Math.sin((double) (-position) / 180 * 3.143)),
(float) (yPoint - radius
* Math.cos((double) (-position) / 180 * 3.143)), paint);
canvas.drawText(String.valueOf(position), xPoint, yPoint, paint);
}
public void updateDirection(double azimuth) {
this.position = azimuth;
invalidate();
}
}
Your problem was simply that when you first coded the app, you were programmatically creating your CompassView with this:
compassView = new CompassView(this);
setContentView(compassView);
When you changed to use XML layouts, that first line was presumably removed. But, you still need to assign your compassView member variable, because you use it in your SensorEventListener's onSensorChanged() method. You were getting a NullPointerException (which you can see by looking at LogCat).
So, in your onCreate() method, after setting the content view, remember to do this:
setContentView(R.layout.activity_display_compass);
compassView = (CompassView)findViewById(R.id.compassView); // <- missing?!