I created a class called ParallaxController to create a parallax effect on three different imageviews with different speeds. This class uses accelerometer sensor and moves images with different speeds in the 'x' axis. My problem is with the performance, I load images in the imageviews via an AsyncTask class - Android Developers Guide - The problem occurs when I use 3 large images in imageviews , when I move the device imageviews move very slowly.
My Codes :
-ParallaxController.java :
public class ParallaxController {
private Activity context;
private View viewBack, viewTop, viewFront;
private float backSpeed, topSpeed, frontSpeed;
private float maxMove;
private SensorManager sensorManager;
private ParallaxSensorEventListener eventListener;
public ParallaxController(Activity context, View viewBack, View viewTop, View viewFront, float backSpeed, float topSpeed, float frontSpeed, float maxMove) {
this.context = context;
this.viewBack = viewBack;
this.viewTop = viewTop;
this.viewFront = viewFront;
this.backSpeed = backSpeed;
this.topSpeed = topSpeed;
this.frontSpeed = frontSpeed;
this.maxMove = maxMove;
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
eventListener = new ParallaxSensorEventListener();
}
public void startControl() {
sensorManager.registerListener(eventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_GAME);
}
public void stopControl() {
sensorManager.unregisterListener(eventListener);
}
private class ParallaxSensorEventListener implements SensorEventListener {
float lastPitch = 0;
public boolean isTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
float pitch;
if (isTablet(context)) {
pitch = sensorEvent.values[2];
} else {
pitch = sensorEvent.values[1];
}
if (pitch > 45 || pitch < -45)
return;
float dp = pitch - lastPitch;
if (dp == 0)
return;
float move = (dp * maxMove) / -45.0f;
float maxSpeed = Math.max(backSpeed, Math.max(frontSpeed, topSpeed));
Display display=context.getWindowManager().getDefaultDisplay();
Point size=new Point();
display.getSize(size);
float backX = (move / maxSpeed) * backSpeed;
float topX = (move / maxSpeed) * topSpeed;
float frontX = (move / maxSpeed) * frontSpeed;
if (viewBack != null) {
float backSpace=(viewBack.getWidth()*(viewBack.getScaleX()-1))/2;
if (viewBack.getX() + backX-backSpace > 0 || viewBack.getX() + backX +viewBack.getWidth()+backSpace < size.x)
return;
} else backX=0;
if (viewFront != null) {
float frontSpace=(viewFront.getWidth()*(viewFront.getScaleX()-1))/2;
if (viewFront.getX() + frontX - frontSpace> 0 || viewFront.getX() + frontX +viewFront.getWidth()+frontSpace < size.x)
return;
} else frontX=0;
if (viewTop != null) {
float topSpace=(viewTop.getWidth()*(viewTop.getScaleX()-1))/2;
if (viewTop.getX() + topX - topSpace > 0 || viewTop.getX() + topX +viewTop.getWidth()+topSpace < size.x)
return;
} else topX=0;
if (backX!=0) viewBack.setX(viewBack.getX() + backX);
if (frontX!=0) viewFront.setX(viewFront.getX() + frontX);
if (topX!=0) viewTop.setX(viewTop.getX() + topX);
lastPitch = pitch;
Log.d("TAG",viewBack.getX()+"");
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
}
MainActivity.java :
public class MainActivity extends AppCompatActivity {
private ParallaxController pc;
private ImageView iv_0;
private ImageView iv_1;
private ImageView iv_2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_0 = (ImageView) findViewById(R.id.iv_0);
iv_1 = (ImageView) findViewById(R.id.iv_1);
iv_2 = (ImageView) findViewById(R.id.iv_2);
setImage(iv_0, R.drawable.a);
setImage(iv_1, R.drawable.b);
setImage(iv_2, R.drawable.c);
scale(iv_0, 1.4f);
scale(iv_1, 1.4f);
scale(iv_2, 1.4f);
pc = new ParallaxController(this,iv_0,iv_1,iv_2, 1 , 3 , 5 , 250);
pc.startControl();
}
private void setImage(ImageView iv, int res){
BitmapWorkerTask task = new BitmapWorkerTask(iv);
task.execute(res);
}
private void scale(View view,float scale){
view.setScaleX(scale);
view.setScaleY(scale);
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
}
}
activity_main.xml
<FrameLayout 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" tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iv_0"
android:layout_gravity="center"
android:scaleType="fitXY"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iv_1"
android:layout_gravity="center" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iv_2"
android:layout_gravity="center"/>
Note: The Activity is Always LandScape.
Thanks ... :)
Related
I'm currently programming my first java game in Android studio. I want to do a setContentview of my activity layout and my gameView. I want to print on screen some controls to move my character, but all I get is my draw() method on screen.
My game activity
public class GameActivity extends AppCompatActivity {
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//setContentView(R.layout.activity_game);
gameView = new GameView(this);
setContentView(gameView);
}
#Override
protected void onPause() {
super.onPause();
gameView.pause();
}
#Override
protected void onResume() {
super.onResume();
gameView.resume();
}
}
My game view
public class GameView extends SurfaceView implements Runnable{
SurfaceHolder surfaceHolder;
String WALK_DIR = "";
private final long NS_perSec = 1000000000;
private int currentFrame = 0;
private static long FPS = 0;
private long timePassed;
public Bitmap character, arrow_up, arrow_down, arrow_left, arrow_right;
private Thread thread;
private boolean isPlaying;
private boolean isMoving = false;
private final float WALK_SPD = 100;
float startPositionX, startPositionY;
float arrowX = 45, arrowY = 30;
Paint paint;
//Sprite Animation
private int frameWidth = 100;
private int frameHeight = 400;
private int frameCount = 4;
public Rect frameToDraw = new Rect(
0,0, frameWidth, frameHeight/frameCount);
RectF whereToDraw = new RectF(
0, 0,
startPositionX + frameWidth,
frameHeight);
public static float screenRatioX, screenRatioY;
private long lastFrameChangeTime = 0l;
public GameView(Context context) {
super(context);
surfaceHolder = getHolder();
Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenRatioX = size.x;
screenRatioY = size.y;
startPositionX = screenRatioX/2 - frameWidth/2;
startPositionY = screenRatioY/2 - frameHeight/8;
paint = new Paint();
character = BitmapFactory.decodeResource(this.getResources(), R.drawable.character_sprite);
arrow_right = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_right);
arrow_down = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_down);
arrow_left = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_left);
arrow_up = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_up);
character = Bitmap.createScaledBitmap(character,frameWidth * frameCount,frameHeight,false);
arrow_right = Bitmap.createScaledBitmap(arrow_right,(int)arrowX,(int)arrowY,false);
arrow_down = Bitmap.createScaledBitmap(arrow_down,(int)arrowY,(int)arrowX,false);
arrow_left = Bitmap.createScaledBitmap(arrow_left,(int)arrowX,(int)arrowY,false);
arrow_up = Bitmap.createScaledBitmap(arrow_up,(int)arrowY,(int)arrowX,false);
}
#Override
public void run() {
isPlaying = true;
while (isPlaying){
long startLoop = System.nanoTime();
update();
draw();
sleep();
timePassed = System.nanoTime() - startLoop;
if(timePassed >= 1){
FPS = (long) (NS_perSec / timePassed);
}
}
}
private void update(){
if(isMoving) {
startPositionX = startPositionX + (WALK_SPD / FPS);
if (startPositionX > screenRatioX){
startPositionY += (int) frameHeight;
startPositionX = 10;
}
if (startPositionY + frameHeight > screenRatioY){
startPositionY = 10;
}
}
}
private void draw(){
if (surfaceHolder.getSurface().isValid()){
Canvas canvas = getHolder().lockCanvas();
canvas.drawColor(Color.WHITE);
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 249, 129, 0));
// Make the text a bit bigger
paint.setTextSize(45);
canvas.drawText("FPS:" + FPS, 20, 40, paint);
whereToDraw.set((int)startPositionX, (int)startPositionY,(int)startPositionX + frameWidth,(int)startPositionY + frameHeight/frameCount);
getCurrentFrame();
canvas.drawBitmap(character, frameToDraw, whereToDraw, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void getCurrentFrame(){
long time = System.nanoTime();
if(isMoving) {
if ( time > lastFrameChangeTime + NS_perSec/10) {
lastFrameChangeTime = time;
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0;
}
}
frameToDraw.left = currentFrame * frameWidth;
frameToDraw.right = frameToDraw.left + frameWidth;
frameToDraw.top = 3 * frameHeight/4;
frameToDraw.bottom = frameToDraw.top + frameHeight/4;
}
}
private void sleep(){
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void resume(){
isPlaying = true;
thread = new Thread(this);
thread.start();
}
public void pause(){
isPlaying = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
Log.e("ERROR", "Joining Thread");
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//return super.onTouchEvent(event);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
isMoving = true;
break;
case MotionEvent.ACTION_UP:
isMoving = false;
break;
}
return true;
}
}
game activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GameActivity">
<ImageView
android:id="#+id/arrow_left"
android:layout_width="45dp"
android:layout_height="30dp"
android:translationZ="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.861"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.858"
app:srcCompat="#drawable/arrow_left" />
<ImageView
android:id="#+id/arrow_right"
android:layout_width="45dp"
android:layout_height="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.95"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.858"
app:srcCompat="#drawable/arrow_right" />
<ImageView
android:id="#+id/arrow_down"
android:layout_width="30dp"
android:layout_height="45dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.897"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.956"
app:srcCompat="#drawable/arrow_down" />
<ImageView
android:id="#+id/arrow_up"
android:layout_width="30dp"
android:layout_height="45dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.897"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.789"
app:srcCompat="#drawable/arrow_up" />
</androidx.constraintlayout.widget.ConstraintLayout>
What I got in my layout in game activity
What I get in my screen when I run the game
I want the controls to be in the right bottom corner of the screen.
I am trying to make a camera app that shows an overlay when a picture is taken.
When this overlay is shown the other UI components (except for the FrameLayout that shows the picture) should go invisible.
But it seems that while my 2 imagebuttons go invisble, my imageview(ivCompass) doesn't.
Here is the code that gets called when a picture is taken
Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//create a new intent...
String path = createFile(data);
intent = new Intent();
intent.putExtra("path", path);
mBearingProvider.updateBearing();
bearing = mBearingProvider.getBearing();
cardinalDirection = bearingToString(bearing);
//((TextView) findViewById(R.id.tvPicDirection)).setText(cardinalDirection);
Log.e("Direction", cardinalDirection + "," + bearing);
findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE);
findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE);
findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE);
findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE);
}
};
And here is the layout file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<ImageButton
android:id="#+id/btnFlash"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_margin="10dp"
android:src="#drawable/camera_flash_on"
android:background="#drawable/circle_flash"
android:onClick="changeFlashMode"/>
<ImageButton
android:id="#+id/btnCapture"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_margin="10dp"
android:src="#drawable/icon_camera"
android:background="#drawable/circle_camera"/>
<ImageView
android:id="#+id/ivCompass"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentRight="true"
android:src="#drawable/camera_compass"
android:background="#android:color/transparent"/>
<RelativeLayout
android:id="#+id/pictureOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#color/alphaBlack"
android:visibility="invisible">
</RelativeLayout>
I think It's just a mistake with naming, syntax or something like that, but I can't seem to find it.
EDIT:
Here is the entire Activity
public class CameraActivity extends AppCompatActivity implements BearingToNorthProvider.ChangeEventListener {
private Camera mCamera;
private CameraView mCameraView;
private float mDist = 0f;
private String flashMode;
private ImageButton flashButton;
private Intent intent;
private BearingToNorthProvider mBearingProvider;
private double bearing;
private double currentBearing = 0d;
private String cardinalDirection = "?";
private final int REQUEST_CODE_ASK_PERMISSIONS = 2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mCamera = getCameraInstance();
mCameraView = new CameraView(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mCameraView);
ImageButton captureButton = (ImageButton) findViewById(R.id.btnCapture);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Camera.Parameters params = mCamera.getParameters();
params.setFlashMode(flashMode);
mCamera.setParameters(params);
mCamera.takePicture(null, null, mPicture);
}
});
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.apiKey), Context.MODE_PRIVATE);
flashMode = sharedPref.getString(getString(R.string.flashMode), Camera.Parameters.FLASH_MODE_OFF);
flashButton = (ImageButton) findViewById(R.id.btnFlash);
setFlashButton();
mBearingProvider = new BearingToNorthProvider(this,this);
mBearingProvider.setChangeEventListener(this);
mBearingProvider.start();
}
#Override
protected void onPause() {
super.onPause();
mBearingProvider.stop();
}
/**
* Helper method to access the camera returns null if it cannot get the
* camera or does not exist
*
* #return the instance of the camera
*/
private Camera getCameraInstance() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception e) {
Log.e("CamException", e.toString());
}
return camera;
}
Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//create a new intent...
String path = createFile(data);
intent = new Intent();
intent.putExtra("path", path);
mBearingProvider.updateBearing();
bearing = mBearingProvider.getBearing();
cardinalDirection = bearingToString(bearing);
//((TextView) findViewById(R.id.tvPicDirection)).setText(cardinalDirection);
Log.e("Direction", cardinalDirection + "," + bearing);
findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE);
findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE);
findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE);
findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE);
}
};
private void confirmPicture(View v) {
/*String direction = String.valueOf(((TextView) findViewById(R.id.tvPicDirection)).getText());
String description = String.valueOf(((EditText) findViewById(R.id.tvPicDescription)).getText());
intent.putExtra("direction", direction);
intent.putExtra("description", description);*/
//close this Activity...
setResult(Activity.RESULT_OK, intent);
finish();
}
//region File Methods
/**
* Method that creates a file from the given byte array and saves the file in the Pictures Directory
* #param data is the array of bytes that represent the picture taken by the camera
* #return the path of created file
*/
private String createFile(byte[] data){
checkFilePermissions();
File picFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "tempPic.jpg" + File.separator);
String path = picFile.getPath();
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(picFile));
bos.write(data);
bos.flush();
bos.close();
return path;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
/**
* Checks the permission for reading to and writing from the external storage
*/
private void checkFilePermissions() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
int hasWriteExternalStoragePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteExternalStoragePermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
return;
}
}
}
//endregion
//region Zoom Methods
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get the pointer ID
Camera.Parameters params = mCamera.getParameters();
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
if (action == MotionEvent.ACTION_POINTER_DOWN) {
mDist = getFingerSpacing(event);
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
mCamera.cancelAutoFocus();
handleZoom(event, params);
}
} else {
// handle single touch events
if (action == MotionEvent.ACTION_UP) {
handleFocus(event, params);
}
}
return true;
}
private void handleZoom(MotionEvent event, Camera.Parameters params) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
float newDist = getFingerSpacing(event);
if (newDist > mDist) {
//zoom in
if (zoom < maxZoom)
zoom++;
} else if (newDist < mDist) {
//zoom out
if (zoom > 0)
zoom--;
}
mDist = newDist;
params.setZoom(zoom);
mCamera.setParameters(params);
}
public void handleFocus(MotionEvent event, Camera.Parameters params) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean b, Camera camera) {
// currently set to auto-focus on single touch
}
});
}
}
/** Determine the space between the first two fingers */
private float getFingerSpacing(MotionEvent event) {
double x = event.getX(0) - event.getX(1);
double y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
//endregion
//region Flash Methods
public void changeFlashMode(View v) {
switch (flashMode) {
case Camera.Parameters.FLASH_MODE_ON :
flashMode = Camera.Parameters.FLASH_MODE_AUTO;
break;
case Camera.Parameters.FLASH_MODE_AUTO :
flashMode = Camera.Parameters.FLASH_MODE_OFF;
break;
case Camera.Parameters.FLASH_MODE_OFF :
flashMode = Camera.Parameters.FLASH_MODE_ON;;
break;
}
SharedPreferences sharedPref = getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(getString(R.string.flashMode), flashMode);
editor.commit();
setFlashButton();
}
public void setFlashButton() {
switch (flashMode) {
case Camera.Parameters.FLASH_MODE_ON :
flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_on));
break;
case Camera.Parameters.FLASH_MODE_AUTO :
flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_auto));
break;
case Camera.Parameters.FLASH_MODE_OFF :
flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_off));
break;
}
}
//endregion
//region Bearing Methods
/**
* Method that gives a cardinal direction based on the current bearing to the true north
* #param bearing is the bearing to the true north
* #return cardinal direction that belongs to the bearing
*/
private String bearingToString(Double bearing) {
String strHeading = "?";
if (isBetween(bearing,-180.0,-157.5)) { strHeading = "South"; }
else if (isBetween(bearing,-157.5,-112.5)) { strHeading = "SouthWest"; }
else if (isBetween(bearing,-112.5,-67.5)) { strHeading = "West"; }
else if (isBetween(bearing,-67.5,-22.5)) { strHeading = "NorthWest"; }
else if (isBetween(bearing,-22.5,22.5)) { strHeading = "North"; }
else if (isBetween(bearing,22.5,67.5)) { strHeading = "NorthEast"; }
else if (isBetween(bearing,67.5,112.5)) { strHeading = "East"; }
else if (isBetween(bearing,112.5,157.5)) { strHeading = "SouthEast"; }
else if (isBetween(bearing,157.5,180.0)) { strHeading = "South"; }
return strHeading;
}
/**
* Method that checks if a certain number is in a certain range of numbers
* #param x is the number to check
* #param lower is the number that defines the lower boundary of the number range
* #param upper is the number that defines the upper boundary of the number range
* #return true if the number is between the other numbers, false otherwise
*/
private boolean isBetween(double x, double lower, double upper) {
return lower <= x && x <= upper;
}
/*
Method that triggers when the bearing changes, it sets the current bearing and sends an updated context to the provider
*/
#Override
public void onBearingChanged(double bearing) {
this.bearing = bearing;
mBearingProvider.setContext(this);
ImageView image = (ImageView) findViewById(R.id.ivCompass);
// create a rotation animation (reverse turn degree degrees)
if (bearing < 0) {
bearing += 360;
}
RotateAnimation ra = new RotateAnimation((float)currentBearing,(float)-bearing, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF,0.5f);
// how long the animation will take place
ra.setDuration(210);
// set the animation after the end of the reservation status
ra.setFillAfter(true);
// Start the animation
image.startAnimation(ra);
currentBearing = -bearing;
mBearingProvider.setContext(this);
}
//endregion
}
EDIT 2:
I have made a small change to the onBearingChanged Method and now the compass is still visible, but thinks it's invisible and isn't moving because of my new if statement
#Override
public void onBearingChanged(double bearing) {
this.bearing = bearing;
mBearingProvider.setContext(this);
ImageView image = (ImageView) findViewById(R.id.ivCompass);
if (image.getVisibility() == View.VISIBLE) {
// create a rotation animation (reverse turn degree degrees)
if (bearing < 0) {
bearing += 360;
}
RotateAnimation ra = new RotateAnimation((float) currentBearing, (float) -bearing, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// how long the animation will take place
ra.setDuration(210);
// set the animation after the end of the reservation status
ra.setFillAfter(true);
// Start the animation
image.startAnimation(ra);
currentBearing = -bearing;
}
mBearingProvider.setContext(this);
}
If you have some kind of animation, the animation probably intefieres on the calling to invisibility. Try with this just before calling INVISIBLE:
findViewById(R.id.ivCompass).clearAnimation();
I am using a FrameLayout to carry out the swipe gesture of each of my cardviews, but the cards do not always dismiss. And sometimes they just hang on the left or the right until the user swipes the card a second time.
How can I fix this?
MyFrameLayout:
public class MyFrameLayout extends FrameLayout {
private static int mWidth = 200;
MyFrameLayout touchFrameLayout;
// Constructors
// i call "initialize(context)" in all of them
private void initialize(Context context) {
setOnTouchListener(mTouchListener);
touchFrameLayout = this;
}
private float mDisplacementX;
private float mDisplacementY;
private float mInitialTx;
private boolean mTracking;
private OnTouchListener mTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mWidth = (int) touchFrameLayout.getLayoutParams().width;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDisplacementX = event.getRawX();
mDisplacementY = event.getRawY();
mInitialTx = getTranslationX();
return true;
case MotionEvent.ACTION_MOVE:
// get the delta distance in X and Y direction
float deltaX = event.getRawX() - mDisplacementX;
float deltaY = event.getRawY() - mDisplacementY;
// updatePressedState(false);
// set the touch and cancel event
if ((Math.abs(deltaX) > ViewConfiguration.get(getContext())
.getScaledTouchSlop() * 2 && Math.abs(deltaY) < Math
.abs(deltaX) / 2)
|| mTracking) {
mTracking = true;
if (getTranslationX() <= mWidth / 2
&& getTranslationX() >= -(mWidth / 2)) {
setTranslationX(mInitialTx + deltaX);
return true;
}
}
break;
case MotionEvent.ACTION_UP:
if (mTracking) {
mTracking = false;
float currentTranslateX = getTranslationX();
if (currentTranslateX > (mWidth/10)) {
rightSwipe();
} else if (currentTranslateX < -(mWidth*10)) {
leftSwipe();
}
// comment this line if you don't want your frame layout to
// take its original position after releasing the touch
setTranslationX(0);
return true;
} else {
// handle click event
setTranslationX(0);
}
break;
}
return false;
}
};
private void rightSwipe() {
Toast.makeText(this.getContext(), "Swipe to the right",
Toast.LENGTH_SHORT).show();
DeleteCard();
}
private void leftSwipe() {
Toast.makeText(this.getContext(), "Swipe to the left",
Toast.LENGTH_SHORT).show();
DeleteCard();
}
}
Xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.testing.android.layout.MyFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/taskViewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:cardElevation="8dp"
app:cardPreventCornerOverlap="false"
card_view:cardCornerRadius="8dp">
</android.support.v7.widget.CardView>
</com.example.testing.android.layout.MyFrameLayout>
</RelativeLayout>
I adapted romannurik's Android-SwipeToDismiss to do exactly that.
The code is on github with a woking sample application, and consists of:
A subclass of RecyclerView.OnItemTouchListener that listens to touch events and detects when an item is being swiped, animating it accordingly.
A SwipeListener that is called in order to know if an item can be dismissed and called again when items are dismissed.
To use it, copy the class SwipeableRecyclerViewTouchListener to your project and use it like this
mAdapter = new CardViewAdapter(mItems);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
SwipeableRecyclerViewTouchListener swipeTouchListener =
new SwipeableRecyclerViewTouchListener(mRecyclerView,
new SwipeableRecyclerViewTouchListener.SwipeListener() {
#Override
public boolean canSwipe(int position) {
return true;
}
#Override
public void onDismissedBySwipeLeft(RecyclerView recyclerView, int[] reverseSortedPositions) {
for (int position : reverseSortedPositions) {
mItems.remove(position);
mAdapter.notifyItemRemoved(position);
}
mAdapter.notifyDataSetChanged();
}
#Override
public void onDismissedBySwipeRight(RecyclerView recyclerView, int[] reverseSortedPositions) {
for (int position : reverseSortedPositions) {
mItems.remove(position);
mAdapter.notifyItemRemoved(position);
}
mAdapter.notifyDataSetChanged();
}
});
mRecyclerView.addOnItemTouchListener(swipeTouchListener);
}
This can also be useful : http://www.getgoodcode.com/2013/06/swipe-to-dismiss-google-style/
Hope it works for you !
i made custom Image View, where a simple line is drawn. This drawing is triggeed by a button.
When triggered the onClicklistener isnt working anymore. But on the screen the button remains clickable, the buttons changes to a blueish color when clicked (default Android button). I think the problem might be in the "drawLine()" when the "setContentView(R.layout.activity_main);" is called, but not sure why and how to get rid of it. Hope you can help.
MainActivity.java
public class MainActivity extends ActionBarActivity implements OnTouchListener,
OnClickListener {
private int number;
private Handler handler;
private boolean Running = true;
private int endX = 50;
private int endY = 500;
private int startX = 50;
private int startY = 50;
private int frames = 25;
ImageView Line01;
Button buttonLineDrawer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Line01 = (ImageView) findViewById(R.id.Line01);
Line01.setVisibility(View.GONE);
Button buttonLineDrawer;
buttonLineDrawer = (Button) findViewById(R.id.buttonLineDrawer);
buttonLineDrawer.setOnClickListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "klick", Toast.LENGTH_LONG)
.show();
Running = true;
number = 0;
drawLine();
}
public void drawLine() {
Line01.setVisibility(View.VISIBLE);
handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
while (Running) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
int coordX = ((((endX - startX) / frames) * number))
+ startX;
int coordY = ((((endY - startY) / frames) * number))
+ startY;
number += 1;
CustomDraw.setCoordinates(startX, startY, coordX,
coordY);
setContentView(R.layout.activity_main);
if ((coordX - endX) == 0 && coordY - endY == 0) {
Running = false;
}
}
});
}
}
};
new Thread(runnable).start();
}}
activity_main.xml
<RelativeLayout 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:background="#FF8800"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="de.trialar.linedrawer.MainActivity" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="#+id/buttonLineDrawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<de.trialar.linedrawer.CustomDraw
android:id="#+id/Line01"
android:layout_width="400dp"
android:layout_height="400dp" />
</LinearLayout></RelativeLayout>
CustomDraw.java
public class CustomDraw extends ImageView {
Paint paint = new Paint();
private static int endX= 500;
private static int endY= 500;
private static int startX= 50;
private static int startY= 50;
static Context context;
public CustomDraw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
public CustomDraw(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDraw(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
canvas.drawLine(startX, startY, endX, endY, paint);
}
public static void setCoordinates(int startX, int startY, int endX, int endY) {
System.out.println("SetCoordinates");
CustomDraw.endX = endX;
CustomDraw.endY = endY;
CustomDraw.startX = startX;
CustomDraw.startY = startY;
}}
i think i found the problem: when you start the handler.run() you set a new Layout:
public void run() {
int coordX = ((((endX - startX) / frames) * number)) + startX;
int coordY = ((((endY - startY) / frames) * number)) + startY;
number += 1;
CustomDraw.setCoordinates(startX, startY, coordX, coordY);
//maybe this is wrong
setContentView(R.layout.activity_main);
//try instead:
CustomDraw.invalidate();
if ((coordX - endX) == 0 && coordY - endY == 0) {
Running = false;
}
}
i don't get why you set a new layout... i guess you want to repaint the button... that's why i added a 'repaint'-line in that code-piece above
In my android app, I have a crop image. So, I programming a custom view as my crop box. I can move the crop box. But my problem is how can I drag border of the crop box and change width and height of it. How can I do it?
Attr Class:
public class Attr {
public static final float CROP_BOX_START_X = 5;
public static final float CROP_BOX_START_Y = 5;
public static final float CROP_BOX_END_X = 305;
public static final float CROP_BOX_END_Y = 105;
}
CropBox Class:
public class CropBox extends View {
private Paint paint = new Paint();
public CropBox(Context context) {
super(context);
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
float[][] circleXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2},
{Attr.CROP_BOX_END_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2}
};
float[][] lineXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y}
};
paint.setColor(Color.CYAN);
paint.setStrokeWidth(1);
for(int i = 0 ; i < circleXY.length ; i++)
canvas.drawCircle(circleXY[i][0], circleXY[i][1], 5, paint);
paint.setStrokeWidth(2);
for(int i = 0 ; i < lineXY.length ; i++)
canvas.drawLine(lineXY[i][0], lineXY[i][2], lineXY[i][2], lineXY[i][3], paint);
}
}
CropTestActivity Class:
public class CropTestActivity extends Activity {
private ImageView imageView;
private CropBox cropBox;
private RelativeLayout relativeLayout;
private RelativeLayout.LayoutParams layoutParams;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBox = new CropBox(this);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
layoutParams.leftMargin = imageView.getWidth() / 2 - (int)((Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2) + imageView.getLeft();
layoutParams.topMargin = imageView.getHeight() / 2 - (int)((Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2) + imageView.getTop();
}
});
relativeLayout.addView(cropBox, layoutParams);
cropBox.setOnTouchListener(new Crop(imageView));
}
}
Crop Class:
public class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private int mode = NONE;
private float cropBoxStartX = Attr.CROP_BOX_START_X;
private float cropBoxStartY = Attr.CROP_BOX_START_Y;
private float cropBoxEndX = Attr.CROP_BOX_END_X;
private float cropBoxEndY = Attr.CROP_BOX_END_Y;
private ImageView imageView;
private PointF start = new PointF();
public Crop(ImageView imageView) {
this.imageView = imageView;
}
public boolean onTouch(View view, MotionEvent event) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
if(event.getX() > cropBoxStartX && event.getX() < cropBoxEndX && event.getY() > cropBoxStartY && event.getY() < cropBoxEndY)
mode = BOX_DRAG;
else if(event.getX() == cropBoxStartX || event.getX() == cropBoxEndX || event.getY() == cropBoxStartY || event.getY() == cropBoxEndY)
mode = BORDER_DRAG;
else
mode = NONE;
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == BOX_DRAG) {
layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
while(layoutParams.topMargin + 5 < imageView.getTop())
layoutParams.topMargin++;
while(layoutParams.leftMargin + (cropBoxEndX - cropBoxStartX + 5) > imageView.getRight())
layoutParams.leftMargin--;
while(layoutParams.topMargin + (cropBoxEndY - cropBoxStartY + 5) > imageView.getBottom())
layoutParams.topMargin--;
while(layoutParams.leftMargin + 5 < imageView.getLeft())
layoutParams.leftMargin++;
}
else if(mode == BORDER_DRAG) {
}
break;
}
view.setLayoutParams(layoutParams);
return true;
}
}
Layout XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/crop_test_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/android_image"
android:src="#drawable/android"
android:layout_width="fill_parent"
android:layout_height="300dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:contentDescription="#string/android_image_description" >
</ImageView>
</RelativeLayout>
Before Resize:
After Resize:
Thanks for your help.
Following is the solution,
Modified onCreate from Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBox = new CropBox(this, imageView);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
relativeLayout.addView(cropBox);
}
Modified CropBox class:
public class CropBox extends View {
private static final int CROP_BOX_START_X = 5;
private static final int CROP_BOX_START_Y = 5;
private static final int CROP_BOX_END_X = 305;
private static final int CROP_BOX_END_Y = 105;
private static final int DRAG_SQUARE = 75;
public ImageView mImageView;
boolean mIsFirstClick = false;
private Paint paint = new Paint();
private Rect mRect;
public CropBox(Context context, ImageView aBaseView) {
super(context);
mImageView = aBaseView;
mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y, CROP_BOX_END_X, CROP_BOX_END_Y);
setOnTouchListener(new Crop());
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
paint.setStrokeWidth(2);
paint.setColor(Color.CYAN);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(mRect, paint);
canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE,
mRect.right, mRect.bottom-DRAG_SQUARE, paint);
canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE,
mRect.right-DRAG_SQUARE, mRect.bottom, paint);
}
class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private int mode = NONE;
private PointF start = new PointF();
public boolean onTouch(View view, MotionEvent event) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
if((event.getX() <= mRect.right && event.getX() >=(mRect.right - DRAG_SQUARE))
&& (event.getY() >= mRect.top && event.getY() >=(mRect.bottom - DRAG_SQUARE))){
mode = BORDER_DRAG;
mIsFirstClick = false;
}
else if(mRect.contains((int)event.getX(), (int)event.getY())) {
mode = BOX_DRAG;
if (mIsFirstClick){
mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y,
CROP_BOX_END_X, CROP_BOX_END_Y);
mIsFirstClick = false;
} else {
mIsFirstClick = true;
}
}
else{
mode = NONE;
mIsFirstClick = true;
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
mIsFirstClick = false;
if(mode == BOX_DRAG) {
layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
}
else if(mode == BORDER_DRAG) {
if (event.getX() > view.getLeft() && event.getY() > view.getTop()){
mRect.right = (int) event.getX();
mRect.bottom = (int) event.getY();
}
}
while(layoutParams.topMargin + 5 < mImageView.getTop())
layoutParams.topMargin++;
while(layoutParams.leftMargin + mRect.right > mImageView.getRight())
layoutParams.leftMargin--;
while(layoutParams.topMargin + mRect.bottom > mImageView.getBottom())
layoutParams.topMargin--;
while(layoutParams.leftMargin + 5 < mImageView.getLeft())
layoutParams.leftMargin++;
break;
}
view.setLayoutParams(layoutParams);
invalidate();
return true;
}
}
}
Some points I would like to mention.
Merged Attr and Crop in CropBox
No need of creating a rectangle from lines. You can use Rect.
Never initialize an array/object in Draw method
Added a feature: if double touched on rectangle it returns to original position
There might be some hitches about the restricting the rect in imageview. I am sure you can fix those... :)
Other than this there is another interesting way using scaling of canvas Image in Canvas with touch events
Use that class instead of Cropbox and try it.
Hope it helps..