Custom shaped buttons in Android - Am I doing it right? - android

I'm trying to implement four custom shaped buttons like you can see in the following picture:
What I did so far: I took 4 different pictures - each with only one color visible (see above). The other part of the image is transparent. This has the result, that I have four pictures with the same size.
Now I used a relative-layout where all my 4 pictures are added into imageviews on the same position. Because of the transparency, I can see the desired picture.
For my ImageViews I've implemented onTouchListener with the following content:
private class ImageOnTouchListener implements View.OnTouchListener {
private int categoryId;
public ImageOnTouchListener(int categoryId) {
this.categoryId = categoryId;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
int x = (int) event.getX();
int y = (int) event.getY();
boolean isInsideBitmap = x < bmp.getWidth() && y < bmp.getHeight() && x >= 0 && y >= 0;
boolean isActionUp = event.getAction() == MotionEvent.ACTION_UP;
if (isInsideBitmap) {
int color = bmp.getPixel(x, y);
bmp.recycle();
if (color == Color.TRANSPARENT){
return false;
}
else {
if (isActionUp) {
buttonClick();
}
}
}else{
bmp.recycle();
}
return true;
}
}
This approach works but it consumes a lot of memory as I'm always creating a bitmap when I move my finger. I'm not quite sure if this is the best way to implement this. Is there anything I can do different which might result in a more efficient way?

Using the fact that you can tell whether or not coordinates belong in a circle with the equation x² + y² <= radius² when the circle's center is (0, 0), I think the following should work.
public class ImageOnTouchListener implements View.OnTouchListener {
// TODO Adjust this value
private static int QUADRANT_RADIUS = 100; // in pixels
// TODO Adjust this value
private static int SPACE_BETWEEN_QUADRANTS = 5; // in pixels
#Override
public boolean onTouch(View v, MotionEvent event) {
int relativeX = (int) (event.getX() - v.getX());
int relativeY = (int) (event.getY() - v.getY());
int center = QUADRANT_RADIUS + (SPACE_BETWEEN_QUADRANTS / 2);
boolean isInsideCircle = Math.pow(relativeX - center, 2) + Math.pow(relativeY - center, 2) <= Math.pow(center, 2);
boolean isInsideBottomLeftQuadrant = isInsideCircle &&
relativeX <= QUADRANT_RADIUS &&
relativeY >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS;
boolean isInsideBottomRightQuadrant = isInsideCircle &&
relativeX >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS &&
relativeY >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS;
boolean isInsideTopLeftQuadrant = isInsideCircle &&
relativeX <= QUADRANT_RADIUS &&
relativeY <= QUADRANT_RADIUS;
boolean isInsideTopRightQuadrant = isInsideCircle &&
relativeX >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS &&
relativeY <= QUADRANT_RADIUS;
boolean isActionUp = event.getAction() == MotionEvent.ACTION_UP;
if (isActionUp) {
if (isInsideBottomLeftQuadrant) {
// Handle bottom left quadrant click
buttonClick();
return true;
} else if (isInsideBottomRightQuadrant) {
// Handle bottom right quadrant click
} // etc.
}
return false;
}
}
You'll need to adjust the QUADRANT_RADIUS and SPACE_BETWEEN_QUADRANTS values, and then either handle all touch events from a single view (like my snippet does) or have a slightly different touch listener per image.

Related

Enable and Disable Zoom in ImageView

I'm using https://github.com/chrisbanes/PhotoView to enable pinch to zoom feature on my custom imageview, however, I'm having a problem with implementing the feature on a toggle button. I wanted to make the image zoom-able when the user clicked the toggle button On. After zooming, when the user clicked the zoom button off, the image should still on its zoomed position but will not be zoomable (because I have another onTouchListener where the user may click and detect colors of the image). Problem is, when the toggle button changes state (on to off), the zoom will reset. Please help me and I'm sorry for my terrible explanation. Thank you.
Below is part of my code:
case R.id.btnZoom:
PhotoViewAttacher photoView= new PhotoViewAttacher(ivImageEditor);
photoView.update();
if(btnZoom.isChecked()){
//photoView.setZoomable(true);
}else if (!btnZoom.isChecked()){
photoView.setZoomable(false);
}
break;
Updated: Below is my activity on which I put the onTouch event for the ImageView to get the pixel colors. (whereas the PhotoView library also override onTouch to enable image zooming). I tried to implement the method from this issue: Android PhotoView Keep Zoom After Orientation Change to keep the scale size/ the zoom size on different state of toggle button but I fail to understand what mScreenBase is. I hope you're able to understand what my problem is. Thanks.
ImageEditorActivity.java:
package com.example.satellite.coloridentifier;
public class ImageEditorActivity extends AppCompatActivity implements View.OnClickListener, TextToSpeech.OnInitListener {
public ImageView ivImageEditor, ivColorPicked;
public TextView tvColorName, tvHexCode, tvRGB;
public ToggleButton btnZoom;
public Bitmap myBitmap;
private float ZoomLevel;
private Matrix theMatrix;
// private RectF rectF;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_editor);
btnZoom = (ToggleButton) findViewById(R.id.btnZoom);
btnZoom.setOnClickListener(this);
ivImageEditor = (ImageView) findViewById(R.id.ivImageEditor);
ivColorPicked = (ImageView) findViewById(R.id.ivColorPicked);
tvColorName = (TextView) findViewById(R.id.tvColorName);
tvHexCode = (TextView) findViewById(R.id.tvHexCode);
tvRGB = (TextView) findViewById(R.id.tvRGB);
// get image or get photo if exist
try {
String imagePath = getIntent().getExtras().getString("imagepath");
String photo = getIntent().getExtras().getString("photoUri");
if (imagePath != null) {
myBitmap = BitmapFactory.decodeFile(imagePath);
ivImageEditor.setImageBitmap(myBitmap);
} else if (photo != null) {
Uri photoUri = Uri.parse(photo);
myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), photoUri);
ivImageEditor.setImageBitmap(myBitmap);
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "No image or photo", Toast.LENGTH_SHORT).show();
}
//get pixel colors from image on touched parts
ivImageEditor.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (!(ivImageEditor.getDrawable() == null)) {
int touchX = (int) motionEvent.getX();
int touchY = (int) motionEvent.getY();
ivImageEditor.buildDrawingCache();
Bitmap bitmap = ivImageEditor.getDrawingCache();
if (touchX > 0 && touchY > 0 && touchX < bitmap.getWidth() && touchY < bitmap.getHeight()) {
int pixel = bitmap.getPixel(touchX, touchY);
int red = Color.red(pixel);
int myRed = (int) Math.round(red * 100.0 / 255);
int green = Color.green(pixel);
int myGreen = (int) Math.round(green * 100.0 / 255);
int blue = Color.blue(pixel);
int myBlue = (int) Math.round(blue * 100.0 / 255);
updateColorRGB(red, green, blue);
updateColorPercentage(myRed, myGreen, myBlue);
}
}
return true;
}
});
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnZoom:
final PhotoViewAttacher photoView = new PhotoViewAttacher(ivImageEditor);
if (btnZoom.isChecked()) {
photoView.setOnMatrixChangeListener(new PhotoViewAttacher.OnMatrixChangedListener() {
#Override
public void onMatrixChanged(RectF rectF) {
theMatrix = photoView.getDisplayMatrix();
float[] theFloat = new float[9];
theMatrix.getValues( theFloat );
RectF theRect = photoView.getDisplayRect();
if (theRect != null)
{
if( theRect.left > ( ivImageEditor.getWidth() / 2 ) || ( theRect.left >= 0 ) )
{
theRect.left = 0;
}
else
{
theRect.left = ( theRect.left - ( ivImageEditor.getWidth() / 2 ) ) / mScreenBase;
}
if( theRect.top > ( ivImageEditor.getHeight() / 2 ) || ( theRect.top >= 0 ) )
{
theRect.top = 0;
}
else
{
theRect.top = ( theRect.top - ( ivImageEditor.getHeight() / 2 ) ) / mScreenBase;
}
ZoomLevel = photoView.getScale();
}
}
});
} else if (!btnZoom.isChecked()) {
photoView.setScaleType(ImageView.ScaleType.CENTER_CROP);
photoView.setDisplayMatrix(theMatrix);
photoView.setScale(ZoomLevel);
// Toast.makeText(this, "level"+ZoomLevel, Toast.LENGTH_SHORT).show();
ivImageEditor.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (!(ivImageEditor.getDrawable() == null)) {
int touchX = (int) motionEvent.getX();
int touchY = (int) motionEvent.getY();
ivImageEditor.buildDrawingCache();
Bitmap bitmap = ivImageEditor.getDrawingCache();
if (touchX > 0 && touchY > 0 && touchX < bitmap.getWidth() && touchY < bitmap.getHeight()) {
int pixel = bitmap.getPixel(touchX, touchY);
int red = Color.red(pixel);
int myRed = (int) Math.round(red * 100.0 / 255);
int green = Color.green(pixel);
int myGreen = (int) Math.round(green * 100.0 / 255);
int blue = Color.blue(pixel);
int myBlue = (int) Math.round(blue * 100.0 / 255);
updateColorRGB(red, green, blue);
updateColorPercentage(myRed, myGreen, myBlue);
}
}
return true;
}
});
}
break;
default:
break;
}
}
public void updateColorPercentage(int redColor, int greenColor, int blueColor) {
DbHelper myDbHelper = new DbHelper(ImageEditorActivity.this);
String colorName = myDbHelper.getColorName(redColor, greenColor, blueColor);
tvColorName.setText(colorName);
tvRGB.setText("(" + String.valueOf(redColor) + "%, " + String.valueOf(greenColor) + "%, " + String.valueOf(blueColor) + "% )");
}
public void updateColorRGB(int red, int green, int blue) {
String colorHex = String.format("#%02X%02X%02X", red, green, blue);
tvHexCode.setText(colorHex);
ivColorPicked.setBackgroundColor(Color.parseColor(colorHex));
}
}
getSuppMatrix(); allows you to save your PhotoView current state. Then disable or enable zoom and reload your PhotoView with the previous state.
Try this to disable zoom on your PhotoView :
Matrix theMatrix = new Matrix();
mPhotoView.getSuppMatrix(theMatrix);
mPhotoView.setZoomable(false);
mPhotoView.setDisplayMatrix(theMatrix);
And this to re enable zoom :
Matrix theMatrix = new Matrix();
mPhotoView.getSuppMatrix(theMatrix);
mPhotoView.setZoomable(true);
mPhotoView.setDisplayMatrix(theMatrix);
Here is the code for update() and setZoomable function from the library. As you can see they reset the drawable matrix every time the update() is called and zoomable is false. So there is no inbuilt way you can persist the state of the image by using this library.
public void setZoomable(boolean zoomable) {
mZoomEnabled = zoomable;
update();
}
public void update() {
if (mZoomEnabled) {
// Update the base matrix using the current drawable
updateBaseMatrix(mImageView.getDrawable());
} else {
// Reset the Matrix...
resetMatrix();
}
}
Here is the similar issue on GitHub Repo : Issue 168. The solution would be to store the focus point and the scale of the zoomed Image and after setting this : photoView.setZoomable(false);, reassign the focus point and the zoomed scale. This might help you implementing the solution : Android PhotoView Keep Zoom After Orientation Change.
Hope it helps!

flood fill coloring on android

i just try to use FloodFill class and i found strange problem with coloring.
Lets start with code:
public class FloodFill {
public void floodFill(Bitmap image, Point node, int targetColor,
int replacementColor) {
int width = image.getWidth();
int height = image.getHeight();
int target = targetColor;
int replacement = replacementColor;
if (target != replacement) {
Queue<Point> queue = new LinkedList<Point>();
do {
int x = node.x;
int y = node.y;
while (x > 0 && image.getPixel(x - 1, y) == target) {
x--;
}
boolean spanUp = false;
boolean spanDown = false;
while (x < width && image.getPixel(x, y) == target) {
image.setPixel(x, y, replacement);
if (!spanUp && y > 0 && image.getPixel(x, y - 1) == target) {
queue.add(new Point(x, y - 1));
spanUp = true;
} else if (spanUp && y > 0
&& image.getPixel(x, y - 1) != target) {
spanUp = false;
}
if (!spanDown && y < height - 1
&& image.getPixel(x, y + 1) == target) {
queue.add(new Point(x, y + 1));
spanDown = true;
} else if (spanDown && y < height - 1
&& image.getPixel(x, y + 1) != target) {
spanDown = false;
}
x++;
}
} while ((node = queue.poll()) != null);
}
}
}
And method where i use FloodFill:
public void colorize()
{
bmp = ((BitmapDrawable)view.getDrawable()).getBitmap();
view.setOnTouchListener(new ImageView.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
int x = (int)event.getX();
int y = (int)event.getY();
...
flood.floodFill(bmp, new Point(x, y), bmp.getPixel(x, y), color);
view.setImageBitmap(bmp);
...
}
});
}
If i try to use standard android color f.g: Color.RED and Color.GREEN, everything works fine. I can replace f.g red with green its works, but if i try to use custom color like this: f.g. Color.rgb(34, 198, 67) i get single point colored instead of filled shape.
Can You help me to find a solution for this problem?
Edit1:
I spoted something interesting. Custom colors seems to be different values on some pixels but i dont know why if i using flood-fill.
Problem solved. Bitmap where i used floodfill was RGB_565. I just convert it to ARGB_8888 and everything work fine.

Touch events after moving a few units

I'm trying to make a touch event that won't be activated until the finger has moved a few units from the initial position.
So far I've set up my onTouch method like this:
private XYEvents xyEvent = new XYEvents();
public boolean motionTracker(MotionEvent event, int n)
{
int note = n;
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
xyEvent.setInitial(event);
playNote(note);
break;
case MotionEvent.ACTION_MOVE:
byte data1;
byte data2;
//I figured I should input a condition to check if the finger has moved a few units before it should start doing stuff like so:
if (xyEvent.getXThreshold(event))
{
int xMod = xyEvent.eventActions(event)[0];
data1 = (byte) (xMod & 0x7f);
data2 = (byte) ((xMod & 0x7f00) >> 8);
xModulation((int)data1, (int)data2);
}
break;
}
This method is the one I'm having problems with:
private float initialX, initialY;
private int xValue;
boolean getXThreshold(MotionEvent event)
{
float deltaX = event.getX();
float threshold = 10;
float condition = (deltaX - initialX);
if(condition <= threshold || condition >= -threshold )
return false;
else
return true;
}
the getXThreshold method seems to do what it's supposed to in another method that looks like this:
public int[] eventActions(MotionEvent event)
{
int value = xValue;
int xNull = 8192;
if(!getXThreshold(event))
xValue = xNull;
if(getXThreshold(event))
xValue = xHandleMove(event, true);
return value;
}
Any suggestions?
/M
It seems that this argument:
if(condition <= threshold || condition >= -threshold )
return false;
else
return true;
Needed to be flipped around, otherwise it always returned false for some reason.
Now it looks like this and works great.
boolean getXThreshold(MotionEvent event)
{
float deltaX = event.getX();
float threshold = 10;
float condition = (deltaX - initialX);
return condition >= threshold || condition <= -threshold;
}
Have a great week!
/M

Touch Listener on a Bitmap Android (Overlay to make parts of bitmap clickable)

Am using bitmap, as rotating dial which consists of sections. Means user can able to rotate bitmap. Now i want to define a singleTap on ImageView for navigating to different sections.
We can do by motionevent x and y, but positions of sections will change dynamically. So, this is not possible.
Is there any other way to do this ?
public class TutorialActivity extends Activity {
private static Bitmap imageOriginal, imageScaled;
private static Matrix matrix;
private ImageView dialer;
private int dialerHeight, dialerWidth;
private GestureDetector detector;
// needed for detecting the inversed rotations
private boolean[] quadrantTouched;
private boolean allowRotating;
View viewNew;
Button aboutus,profile,colleagues,picasa,facebook,twitter,youtube,referral;
int one_piecewidth,one_pieceheight;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
// initialize the matrix only once
if (matrix == null) {
matrix = new Matrix();
} else {
// not needed, you can also post the matrix immediately to restore the old state
matrix.reset();
}
detector = new GestureDetector(this, new MyGestureDetector());
// there is no 0th quadrant, to keep it simple the first value gets ignored
quadrantTouched = new boolean[] { false, false, false, false, false };
allowRotating = true;
//viewNew=LayoutInflater.from(TutorialActivity.this).inflate(R.layout.new_view,null,false);
// load the image only once
if(imageOriginal==null){
//imageOriginal=loadBitmapFromView(viewNew);
imageOriginal=BitmapFactory.decodeResource(getResources(), R.drawable.circular_menu);
}
imageScaled=imageOriginal;
one_piecewidth=((int) imageOriginal.getWidth()/4);
one_pieceheight=((int) imageOriginal.getHeight()/4);
dialer = (ImageView) findViewById(R.id.imageView_ring);
dialer.setImageBitmap(imageOriginal);
dialer.setOnTouchListener(new MyOnTouchListener());
dialer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// method called more than once, but the values only need to be initialized one time
if (dialerHeight == 0 || dialerWidth == 0) {
dialerHeight = dialer.getHeight();
dialerWidth = dialer.getWidth();
// resize
Matrix resize = new Matrix();
resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);
// translate to the image view's center
float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
matrix.postTranslate(translateX, translateY);
dialer.setImageBitmap(imageScaled);
dialer.setImageMatrix(matrix);
}
}
});
}
/**
* Rotate the dialer.
*
* #param degrees The degrees, the dialer should get rotated.
*/
private void rotateDialer(float degrees) {
matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);
dialer.setImageMatrix(matrix);
//viewNew.po
}
/**
* #return The angle of the unit circle with the image view's center
*/
private double getAngle(double xTouch, double yTouch) {
double x = xTouch - (dialerWidth / 2d);
double y = dialerHeight - yTouch - (dialerHeight / 2d);
switch (getQuadrant(x, y)) {
case 1:
return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
case 2:
case 3:
return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
case 4:
return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
default:
// ignore, does not happen
return 0;
}
}
/**
* #return The selected quadrant.
*/
private static int getQuadrant(double x, double y) {
if (x >= 0) {
return y >= 0 ? 1 : 4;
} else {
return y >= 0 ? 2 : 3;
}
}
/**
* Simple implementation of an {#link OnTouchListener} for registering the dialer's touch events.
*/
private class MyOnTouchListener implements OnTouchListener {
private double startAngle;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// reset the touched quadrants
for (int i = 0; i < quadrantTouched.length; i++) {
quadrantTouched[i] = false;
}
allowRotating = false;
startAngle = getAngle(event.getX(), event.getY());
Drawable drawable = dialer.getDrawable();
Rect imageBounds = drawable.getBounds();
//original height and width of the bitmap
int intrinsicHeight = drawable.getIntrinsicHeight();
int intrinsicWidth = drawable.getIntrinsicWidth();
//height and width of the visible (scaled) image
int scaledHeight = imageBounds.height();
int scaledWidth = imageBounds.width();
//Find the ratio of the original image to the scaled image
//Should normally be equal unless a disproportionate scaling
//(e.g. fitXY) is used.
float heightRatio = intrinsicHeight / scaledHeight;
float widthRatio = intrinsicWidth / scaledWidth;
//do whatever magic to get your touch point
//MotionEvent event;
//get the distance from the left and top of the image bounds
int scaledImageOffsetX = (int) (event.getX() - imageBounds.left);
int scaledImageOffsetY = (int) (event.getY() - imageBounds.top);
//scale these distances according to the ratio of your scaling
//For example, if the original image is 1.5x the size of the scaled
//image, and your offset is (10, 20), your original image offset
//values should be (15, 30).
int originalImageOffsetX = (int) (scaledImageOffsetX * widthRatio);
int originalImageOffsetY = (int) (scaledImageOffsetY * heightRatio);
break;
case MotionEvent.ACTION_MOVE:
double currentAngle = getAngle(event.getX(), event.getY());
if((float) (startAngle - currentAngle)>1.0)
rotateDialer((float) (startAngle - currentAngle));
startAngle = currentAngle;
break;
case MotionEvent.ACTION_UP:
allowRotating = true;
break;
}
// set the touched quadrant to true
quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;
detector.onTouchEvent(event);
return true;
}
}
/**
* Simple implementation of a {#link SimpleOnGestureListener} for detecting a fling event.
*/
private class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// get the quadrant of the start and the end of the fling
int q1 = getQuadrant(e1.getX() - (dialerWidth / 2), dialerHeight - e1.getY() - (dialerHeight / 2));
int q2 = getQuadrant(e2.getX() - (dialerWidth / 2), dialerHeight - e2.getY() - (dialerHeight / 2));
System.out.println("q1 and q2"+q1+".."+q2);
// the inversed rotations
if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY))
|| (q1 == 3 && q2 == 3)
|| (q1 == 1 && q2 == 3)
|| (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY))
|| ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
|| ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
|| (q1 == 2 && q2 == 4 && quadrantTouched[3])
|| (q1 == 4 && q2 == 2 && quadrantTouched[3])) {
dialer.post(new FlingRunnable(-1 * (velocityX + velocityY)));
} else {
// the normal rotation
dialer.post(new FlingRunnable(velocityX + velocityY));
}
return true;
}
#Override
public boolean onSingleTapUp(MotionEvent e){
int pixel = imageScaled.getPixel((int)(e.getX()),(int)(e.getY()));
System.out.println("pixel"+pixel+"...");
return true;
}
}
/**
* A {#link Runnable} for animating the the dialer's fling.
*/
private class FlingRunnable implements Runnable {
private float velocity;
public FlingRunnable(float velocity) {
this.velocity = velocity;
}
#Override
public void run() {
if (Math.abs(velocity) > 5 && allowRotating) {
rotateDialer(velocity / 75);
velocity /= 1.0666F;
// post this instance again
dialer.post(this);
}
}
}
public static Bitmap loadBitmapFromView(View v) {
System.out.println("vvv"+v+"....");
System.out.println("vvvparams"+v.getMeasuredHeight()+"....");
// System.out.println("vvvparamswidth"+v.getLayoutParams().height+"....");
Bitmap b = Bitmap.createBitmap( 300, 300, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, 300, 300);
v.draw(c);
return b;
}
public Bitmap addView(View v) {
v.setDrawingCacheEnabled(true);
// this is the important code :)
// Without it the view will have a dimension of 0,0 and the bitmap will be null
System.out.println("mesaure spec"+MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
v.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
v.buildDrawingCache(true);
Bitmap b = Bitmap.createBitmap(v.getDrawingCache());
v.setDrawingCacheEnabled(false);
return b;// clear drawing cache
}
public void onResume(){
super.onResume();
}
}
Add this code on touch event:
{
case MotionEvent.ACTION_DOWN:
start = e.getEventTime();
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
stop = e.getEventTime();
break;
}
//delay or use thread
if (stop - start > 1500) {
//your code
}
return true;

Rotation around 3d object on touch using min3d/Rajawali framework (android)

I am working on an algorithm for rotating the camera around a 3D object using the Min3d/Rajawali framework.
With my implementation, the rotation around axis X is not working properly. I think the method setLookAt() is not working properly.
The problem:
When I rotate the sphere vertically, I can't fully see it. For example, turning the planet Earth, I can not fully see the Antarctic, because the algorithm resets the camera down.
Is it possible to realize the camera rotation around an object without using the method "setLookAt"?
I have tried different solutions, but have not been able to get it working correctly.
Below is my code:
initScene:
scene.camera().position.z = 90;
scene.camera().target = raketeOBJ.position();
onTouchEvent:
public boolean onTouchEvent(MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}
if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}
if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;
xpos = me.getX();
ypos = me.getY();
touchTurn = xd / -200f;
touchTurnUp = yd / -200f;
return true;
}
try {
Thread.sleep(15);
} catch (Exception e) {
}
return super.onTouchEvent(me);
}
UpdateScene:
if (touchTurn != 0) {
scene.camera().position.rotateY(touchTurn);
touchTurn = 0;
}
if (touchTurnUp != 0) {
scene.camera().position.rotateX(touchTurnUp);
touchTurnUp = 0;
}
Number3d target = scene.camera.target;
Number3d cp = scene.camera.position.clone();
// move position like target is (0,0,0)
cp.x -= target.x;
cp.y -= target.y;
cp.z -= target.z;
cp.roateX(angle);
// restore offset
cp.x += target.x;
cp.y += target.y;
cp.z += target.z;
scene.camera.position.setAllFrom(cp);
A bit late but in case anyone has trouble with that, like me.
Rajawali offers a camera called ArcballCamera, which does exactly what you/we are trying to do.
In your Renderer add the following:
ArcballCamera arcball = new ArcballCamera(mContext, ((Activity)mContext).findViewById(R.id.contentView));
arcball.setTarget(mObjectGroup); //your 3D Object
arcball.setPosition(0,0,4); //optional
getCurrentScene().replaceAndSwitchCamera(getCurrentCamera(), arcball);
now you can rotate and zoom the object without a dozen lines of code.
setLookAt() needs to be called onDrawFrame if you want the camera to update regularly. But you need to care more about creating a "RotateAroundAnimation". See here for more info: http://www.rozengain.com/blog/2012/03/26/rajawali-tutorial-12-animation-classes/
You'll have to make yourAnimation.setTransformable3D(mCamera), and then it should control your main camera. I often use this methodology and then call the "yourAnimation.start()" on touch, or other external stimulus.
This is my way to rotate 3d model according as x and y
in Render class
public boolean left, right;
public boolean up, down;
and in onDrawFrame
#Override
public void onDrawFrame(GL10 glUnused) {
super.onDrawFrame(glUnused);
// getCurrentCamera().setRotY(getCurrentCamera().getRotY() + 1);
if (left) {
getCurrentCamera().setRotX(getCurrentCamera().getRotX() - 1);
}
if (right) {
getCurrentCamera().setRotX(getCurrentCamera().getRotX() + 1);
}
if (up) {
getCurrentCamera().setRotY(getCurrentCamera().getRotY() - 1);
}
if (down) {
getCurrentCamera().setRotY(getCurrentCamera().getRotY() + 1);
}
}
and in MainActivity
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
xpos = event.getX();
ypos = event.getY();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
mRender.left = false;
mRender.right = false;
mRender.up = false;
mRender.down = false;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
xd = event.getX() - xpos;
yd = event.getY() - ypos;
xpos = event.getX();
ypos = event.getY();
if (xd < 0) {
mRender.up = true;
} else {
mRender.down = true;
}
if (yd < 0) {
mRender.left = true;
} else {
mRender.right = true;
}
}
return super.onTouchEvent(event);
}
by this way, i can rotate my model :)

Categories

Resources