How to move to element and press on in mob app - android

How to move to element, that not visible on the screen using xpath and then click on it in mobile app?
This element located in the bottom, so need to swipe up.

Here is a small example of scroll and click :
public void scrollDown() {
Dimension size = driver.manage().window().getSize();
int startHeight = (int) (size.height * 0.5);
int startWidth = (int) (size.width * 0.5);
int endHeight = (int) (size.height * 0.25);
int endWidth = (int) (size.width * 0.5);
new TouchAction(driver).press(PointOption.point(startWidth, startHeight)).waitAction().moveTo(PointOption.point(endWidth, endHeight)).release().perform();
}
public void scrollUntilElementIsVisible(WebElement element) {
try {
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
boolean flag = element.isDisplayed();
while (!flag) {
scrollDown();
}
element.click();
} catch (NoSuchElementException e) {
System.out.println(e.toString());
} finally {
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
}
}

You need to use findElementByAndroidUIAutomator method. It scrolls to your particular element and when it finds it, you can call click() on this element:
driver.findElementByAndroidUIAutomator("new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(\"" + Element_text + "\").instance(0))");
driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Element_text\")").click();

I did it with:
TouchAction ta = new TouchAction(driver);
ta.press(button).moveTo(x, y).release().perform();

Related

How to detect touch move over a large number of views?

My Requirement : I need to animate a 7*16 LED Display by passing frames through the Android App over BluetoothLE. I have created the design of the display on the app and added empty views with gradient drawable background to them. The colour of these views need to change when my touch moves into them. Adding a touch listener to each view isn't going to help in my case.
What I have achieved : I have a large number of views (100+) added programmatically with a tag set to each of them. I have set an OnTouch Event Handler for the parent view in which these views have been added.
By tracking the absolute coordinates of the touch event (x and y) and comparing with the absolute bounds of a few individual views that I am looping in the touch event handler, I am able to detect hover like touch move (out of bounds to in bounds) over 3-4 views properly.
I have referred the solution from https://stackoverflow.com/a/21903640
Where I am stuck : However, when I try to increase the loop size to cover all the added views, the app response slows down and hover detection fails on most of the views. I know this is happening because of heavy computation in the OnTouch Event Handler which I am not supposed to do.
What I need : I need an improvement on this solution in terms of performance or an alternative way to go about reaching my goal.
Code Snippet
void DrawScreen()
{
for (int column = 0; column < 8; column++)
{
for (int row = 0; row < 17; row++)
{
relativeLayout.AddView(DrawRect(row, column));
}
}
}
View DrawRect(int row, int column)
{
View customView = new View(Context);
GradientDrawable shape = new GradientDrawable();
shape.SetShape(ShapeType.Rectangle);
shape.SetCornerRadii(new float[] { 10, 10, 10, 10, 10, 10, 10, 10 });
shape.SetColor(Color.ParseColor("#3F0000"));
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
param.LeftMargin = ((column-1) * (width + h_spacing)) + h_spacing;
param.Width = width;
param.Height = height;
param.TopMargin = ((row-1) * (height + v_spacing)) + v_spacing;
customView.Background = shape;
customView.LayoutParameters = param;
customView.Tag = (8 - column).ToString() + "," + (17 - row).ToString();
return customView;
}
private void RelativeLayout_Touch(object sender, View.TouchEventArgs e)
{
if(e.Event.Action == MotionEventActions.Up)
{
out_of_bounds = true;
view_in_bound = null;
}
else
{
for (int row = 1; row < 8; row++)
{
for (int column = 1; column < 17; column++)
{
View view = relativeLayout.FindViewWithTag(row.ToString() + "," + column.ToString());
if (CheckInterSection(view, e.Event.RawX, e.Event.RawY))
{
if (out_of_bounds == true)
{
view_in_bound = view;
out_of_bounds = false;
Log.Debug("Touch", "Inside");
//ToggleViewState(view);
}
else
{
Log.Debug("Touch", "Still Inside");
}
}
else
{
if (view == view_in_bound)
{
out_of_bounds = true;
view_in_bound = null;
Log.Debug("Touch", "Outside");
}
}
}
}
}
}
bool CheckInterSection(View view, float rawX, float rawY)
{
int[] location = new int[2];
view.GetLocationOnScreen(location);
int x = location[0] - h_spacing/2;
int y = location[1] - v_spacing/2;
int width = (view.Width + h_spacing/2);
int height = (view.Height + v_spacing/2);
return (!(rawX < x || rawX > (x + width) || rawY < y || rawY > (y + height)));
}
I tried to use trajectory angles to further reduce the loop size but the performance was never up to my expectations and touch events over views were getting missed regularly.
Then I realized that I was going in the wrong direction and found a much simpler solution. Since my views were being added programmatically and were of same size, I knew the coordinates and bounds of each view in the layout. So I divided the layout into a grid and depending upon the touch coordinates, I was able to identify over which section the touch was on. Below is my solution which has been working perfectly. However, I will wait a while till I mark this as a solution since someone could have a better implementation of my technique or an alternative solution.
void DrawScreen()
{
for (int column = 1; column < 17; column++)
{
for (int row = 1; row < 8; row++)
{
relativeLayout.AddView(DrawRect(row, column));
}
}
}
View DrawRect(int row, int column)
{
View customView = new View(Context);
if (!CheckBit(row - 1, column - 1))
{
customView.SetBackgroundResource(Resource.Drawable.off_rounded_btn);
}
else
{
customView.SetBackgroundResource(Resource.Drawable.rounded_btn);
}
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
param.LeftMargin = ((column-1) * (width + h_spacing)) + h_spacing;
param.Width = width;
param.Height = height;
param.TopMargin = ((row-1) * (height + v_spacing)) + v_spacing;
customView.LayoutParameters = param;
customView.Tag = row.ToString() + "," + column.ToString();
return customView;
}
void RelativeLayout_Touch(object sender, View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Up)
{
view_in_bound = null;
}
else
{
int row = CheckTouchArea(e.Event.RawX, e.Event.RawY)[0];
if (row != 0)
{
int column = CheckTouchArea(e.Event.RawX, e.Event.RawY)[1];
check_view = GetView(row, column);
if (check_view != view_in_bound)
{
ChangeViewState(check_view, Touch_CheckBit(row - 1, column - 1), row - 1, column - 1);
view_in_bound = check_view;
}
}
}
}
int[] CheckTouchArea(float rawX, float rawY)
{
int[] tag = new int[2];
int[] location = new int[2];
relativeLayout.GetLocationOnScreen(location);
float x = location[0] + h_padding / 2;
int y = location[1] + v_padding / 2;
float width = relativeLayout.Width - h_padding;
int height = relativeLayout.Height - v_padding;
if ((!(rawX < x || rawX > (x + width) || rawY < y || rawY > (y + height))))
{
int column = (int)Math.Ceiling((rawX - x) * 16 / width);
int row = (int)(Math.Ceiling((rawY - y) * 7 / height));
tag[0] = row;
tag[1] = column;
}
return tag;
}

Not able to find element in pop up in appium

In my app profile I want to select Religion, so when I am clicking on Religion one pop up is opening and I have to choose one element from there,
Only "Index" and "text" values are different for each element, "Id" same for all element.
But while taking "xpath" using "text" and "index" values, not getting the element.
I am using scroll to method also but not getting the element.
Thanks in advance.
You can click by using click method:
driver.findElementByName("your religion name").click();
and scroll by using this method:
public void keepScrollingUntilElementFound(String name) {
for (int i = 0; i < 10; i++) {
if (isElementPresent("name=" + name)) {
break;
} else {
scrollDown();
scrollDown();
}
}
}
public void scrollDown() {
Dimension size = driver.manage().window().getSize();
int x = size.width / 2;
int starty = (int) (size.height * 0.60);
int endy = (int) (size.height * 0.10);
driver.swipe(x, starty, x, endy, 2000);
}
public boolean isElementPresent(String name) {
try {
driver.findElementByName(name);
return true;
} catch (Exception e) {
return false;
}
}
get all the index and its corresponding religions and click by index number
List<WebElement> ls = driver.findElementsById("android:id/numberpicker_input");
System.out.println("no of religions" + ls.size());
ls.get(0).click

Nine-patch content area not working

I'm trying to make a frame for TextView as a cloud. But the content area does not behave as expected. What am i doing wrong?
I have a suggestion that is not working properly because the content area less scale area. So sad. I remade it to handle 9-patch manually. Save pictures without .9.png. Get Bitmap. There are 9-line present. With getPixels calculated padding and set it on the TextView. After that calculating and set LayoutParams.width and LayoutParams.height. Looks a bit ugly, but it works quite quickly, and most importantly correctly.
private int startX=-1;
private int endX=-1;
private int contentW=-1;
private int contentH=-1;
Bitmap bmp=BitmapFactory.decodeResource(getResources(), mIconResId);
int[] pixels=new int[bmp.getWidth()*bmp.getHeight()];
bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(),bmp.getHeight());
for(int i=0;i<bmp.getWidth();i++){
if(startX==-1 && pixels[bmp.getWidth()*(bmp.getHeight()-1)+i]==Color.BLACK){
startX=i;
}
if(startX!=-1 && pixels[bmp.getWidth()*(bmp.getHeight()-1)+i]!=Color.BLACK){
endX=i;
break;
}
}
int startY=-1;
int endY=-1;
for(int i=0;i<bmp.getHeight();i++){
if(startY==-1 && pixels[bmp.getWidth()*(i+1)-1]==Color.BLACK){
startY=i;
}
if(startY!=-1 && pixels[bmp.getWidth()*(i+1)-1]!=Color.BLACK){
endY=i;
break;
}
}
setBackground(new BitmapDrawable(getResources(),Bitmap.createBitmap(bmp, 1, 1, bmp.getWidth()-2, bmp.getHeight()-2)));
contentW=endX-startX;
endX=bmp.getWidth()-endX;
contentH=endY-startY;
endY=bmp.getHeight()-endY;
new Handler().post(new Rannable(){
#Override
public void run() {
int w=textview.getWidth();
int h=textview.getHeight();
if(w>endX-startX){
float k=((float)w)/contentW;
startX=(int) (startX*k);
endX=(int) (endX*k);
}
if(h>endY-startY){
float k=((float)h)/contentH;
startY=(int) (startY*k);
endY=(int) (endY*k);
}
w+=startX+startX;
h+=startY+endY;
textview.setPadding(startX, startY, endX, endY);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(w,h);
textview.setLayoutParams(lp);
}
});
You set good values for right and bottom borders. You just have to set same values for left and top borders, left border = right border and top border = bottom border.
The result in draw9patch:
And here the 9-patch file:
For your information, your image is not really suitable for using with 9-patch format.
I extended/adapted #ahtartam code. I am not sure if it is the cleanest way but it works for me. If someone needs help, just contact me or ask in comments!
public void setTextLayout(int orgW, int orgH,int actW,int actH,int top,int left) {
int startX = -1;
int endX = -1;
int startY = -1;
int endY = -1;
int contentW;
int contentH;
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.image);
int[] pixels = new int[orgW * orgH];
bmp.getPixels(pixels, 0, orgW, 0, 0, orgW, orgH);
for (int i = 0; i < orgW; i++) {
if (startX == -1 && pixels[orgW * (orgH - 1) + i] == Color.BLACK) {
startX = i;
}
if (startX != -1 && pixels[orgW * (orgH - 1) + i] != Color.BLACK) {
endX = i;
break;
}
}
for (int i = 0; i < orgH; i++) {
if (startY == -1 && pixels[orgW * (i + 1) - 1] == Color.BLACK) {
startY = i;
}
if (startY != -1 && pixels[orgW * (i + 1) - 1] != Color.BLACK) {
endY = i;
break;
}
}
m_marvin.setImageDrawable(new BitmapDrawable(getResources(), Bitmap.createBitmap(bmp, 1, 1, orgW - 2, orgH - 2)));
RelativeLayout.LayoutParams rp = (RelativeLayout.LayoutParams) m_marvin.getLayoutParams();
contentW=endX- startX;
contentH=endY-startY;
endX=orgW-endX;
endY=orgH-endY;
double scaleX = ((double)actW) / bmp.getWidth();
double scaleY = ((double)actH) / bmp.getHeight();
startX = (int) (startX * scaleX);
endX = (int) (endX * scaleX);
startY = (int) (startY * scaleY);
endY = (int) (endY * scaleY) ;
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams((int)(contentW*scaleX),(int)(contentH*scaleY));
layoutParams.setMargins(startX+rp.leftMargin+left, startY+rp.topMargin+top, endX+rp.rightMargin, endY+rp.bottomMargin);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE);
m_text.setLayoutParams(layoutParams);
m_text.bringToFront();
}
Instead TextView I use SizeAwareImageView from -> https://stackoverflow.com/a/15538856/1438596
In my case it looks like this->
public class SizeAwareImageView extends ImageView {
MainActivity m_mainActivity;
public SizeAwareImageView(Context context,AttributeSet attrss){
super(context,attrss);
m_mainActivity = (MainActivity)context;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(m_mainActivity.getTextMeasured())return;
// Get image matrix values and place them in an array
float[] f = new float[9];
getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
int top = (int) (imgViewH - actH)/2;
int left = (int) (imgViewW - actW)/2;
if(origW!=actW){
m_mainActivity.setTextMeasured(true);
m_mainActivity.setTextLayout(origW, origH, actW, actH,top,left);
}
}
}
You could use this tool for creating your nine-patch images.

Background for each series GraphView

Im using Graphview and work fine, but now i have a problem.
I would like to have a background for each series that is added to the graph, and not for all series
Is this possible?
This is currently (5 August 2014) not possible on the original GraphView library.
I needed this functionality, so I forked the library and implemented the functionality myself. You can find the updated code on the feature/series_specific_styles branch of my fork:
https://github.com/mobiRic/GraphView/tree/feature/series_specific_styles
Hopefully in future these changes will be pulled into the original library.
The actual code changes are relatively simple.
Added required background fields to GraphViewSeries.GraphViewSeriesStyle
Updated LineGraphView.drawSeries() to look for these fields instead of relying on its own internal values.
I have included full updates below, but the easiest way to view them is on the commit page:
allow different background for each series
Here is the updated GraphViewSeriesStyle class:
static public class GraphViewSeriesStyle {
public int color = 0xff0077cc;
public int thickness = 3;
private ValueDependentColor valueDependentColor;
private final Paint paintBackground;
private boolean drawBackground;
private boolean drawDataPoints;
private float dataPointsRadius = 10f;
public GraphViewSeriesStyle() {
super();
paintBackground = new Paint();
paintBackground.setColor(Color.rgb(20, 40, 60));
paintBackground.setStrokeWidth(4);
paintBackground.setAlpha(128);
}
public GraphViewSeriesStyle(int color, int thickness) {
super();
this.color = color;
this.thickness = thickness;
paintBackground = new Paint();
paintBackground.setColor(Color.rgb(20, 40, 60));
paintBackground.setStrokeWidth(4);
paintBackground.setAlpha(128);
}
public ValueDependentColor getValueDependentColor() {
return valueDependentColor;
}
/**
* the color depends on the value of the data.
* only possible in BarGraphView
* #param valueDependentColor
*/
public void setValueDependentColor(ValueDependentColor valueDependentColor) {
this.valueDependentColor = valueDependentColor;
}
public boolean getDrawBackground() {
return drawBackground;
}
public void setDrawBackground(boolean drawBackground) {
this.drawBackground = drawBackground;
}
public Paint getPaintBackground() {
return paintBackground;
}
public int getBackgroundColor() {
return paintBackground.getColor();
}
/**
* sets the background colour for the series. This is not the background
* colour of the whole graph.
*/
public void setBackgroundColor(int color) {
paintBackground.setColor(color);
}
public float getDataPointsRadius() {
return dataPointsRadius;
}
public boolean getDrawDataPoints() {
return drawDataPoints;
}
/**
* sets the radius of the circles at the data points.
* #see #setDrawDataPoints(boolean)
* #param dataPointsRadius
*/
public void setDataPointsRadius(float dataPointsRadius) {
this.dataPointsRadius = dataPointsRadius;
}
/**
* You can set the flag to let the GraphView draw circles at the data points
* #see #setDataPointsRadius(float)
* #param drawDataPoints
*/
public void setDrawDataPoints(boolean drawDataPoints) {
this.drawDataPoints = drawDataPoints;
}
}
Here is the updated LineGraphView.drawSeries() method:
public void drawSeries(Canvas canvas, GraphViewDataInterface[] values, float graphwidth, float graphheight, float border, double minX, double minY, double diffX, double diffY, float horstart, GraphViewSeriesStyle style) {
// draw background
double lastEndY = 0;
double lastEndX = 0;
// draw data
paint.setStrokeWidth(style.thickness);
paint.setColor(style.color);
Path bgPath = null;
if ((drawBackground) || (style.getDrawBackground())) {
bgPath = new Path();
}
lastEndY = 0;
lastEndX = 0;
float firstX = 0;
for (int i = 0; i < values.length; i++) {
double valY = values[i].getY() - minY;
double ratY = valY / diffY;
double y = graphheight * ratY;
double valX = values[i].getX() - minX;
double ratX = valX / diffX;
double x = graphwidth * ratX;
if (i > 0) {
float startX = (float) lastEndX + (horstart + 1);
float startY = (float) (border - lastEndY) + graphheight;
float endX = (float) x + (horstart + 1);
float endY = (float) (border - y) + graphheight;
// draw data point
if (drawDataPoints) {
//fix: last value was not drawn. Draw here now the end values
canvas.drawCircle(endX, endY, dataPointsRadius, paint);
} else if (style.getDrawDataPoints()) {
canvas.drawCircle(endX, endY, style.getDataPointsRadius(), paint);
}
canvas.drawLine(startX, startY, endX, endY, paint);
if (bgPath != null) {
if (i==1) {
firstX = startX;
bgPath.moveTo(startX, startY);
}
bgPath.lineTo(endX, endY);
}
} else if ((drawDataPoints) || (style.getDrawDataPoints())) {
//fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above)
float first_X = (float) x + (horstart + 1);
float first_Y = (float) (border - y) + graphheight;
if (drawDataPoints) {
canvas.drawCircle(first_X, first_Y, dataPointsRadius, paint);
} else if (style.getDrawDataPoints()) {
canvas.drawCircle(first_X, first_Y, style.getDataPointsRadius(), paint);
}
}
lastEndY = y;
lastEndX = x;
}
if (bgPath != null) {
// end / close path
bgPath.lineTo((float) lastEndX, graphheight + border);
bgPath.lineTo(firstX, graphheight + border);
bgPath.close();
if (style.getDrawBackground()) {
canvas.drawPath(bgPath, style.getPaintBackground());
} else {
canvas.drawPath(bgPath, paintBackground);
}
}
}
For interest, that branch also allows data points to be configured for each series - code changes visible here:
allow datapoint styling for each series

How to move image on curve on touch event in android?

I have drawn a Cubic Curve on canvas using
myPath.cubicTo(10, 10, w, h/2, 10, h-10);
I have four ImageView on that screen and I want to move that ImageViews on the drawn curve when I drag that image with touch.
I have referred the links :
Move Image on Curve Path
Move object on Curve
Move imageview on curve
What I get is, Animation to move the Image on Curve with the duration defined by t.
But I want to move that ImageView on touch in direction of that curve area only.
Following is my Screen :
So, I want all the (x,y) co-ordinates of the curve to move ImageView on that curve only.
Else I want an equation to draw a curve so that I can interpolate x value for the touched y value.
I have goggled a lot but didn't succeed.
Any advice or guidance will help me a lot.
Approach
I would suggest a different approach than using bezier as you would need to reproduce the math for it in order to get the positions.
By using simple trigonometry you can achieve the same visual result but in addition have full control of the positions.
Trigonometry
For example:
THIS ONLINE DEMO produces this result (simplified version for sake of demo):
Define an array with the circles and angle positions instead of y and x positions. You can filter angles later if they (e.g. only show angles between -90 and 90 degrees).
Using angles will make sure they stay ordered when moved.
var balls = [-90, -45, 0, 45]; // example "positions"
To replace the Bezier curve you can do this instead:
/// some setup variables
var xCenter = -80, /// X center of circle
yCenter = canvas.height * 0.5, /// Y center of circle
radius = 220, /// radius of circle
x, y; /// to calculate line position
/// draw half circle
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI);
ctx.stroke();
Now we can use an Y value from mouse move/touch etc. to move around the circles:
/// for demo, mousemove - adopt as needed for touch
canvas.onmousemove = function(e) {
/// get Y position which is used as delta to angle
var rect = demo.getBoundingClientRect();
dlt = e.clientY - rect.top;
/// render the circles in new positions
render();
}
The rendering iterates through the balls array and render them in their angle + delta:
for(var i = 0, angle; i < balls.length; i++) {
angle = balls[i];
pos = getPosfromAngle(angle);
/// draw circles etc. here
}
The magic function is this:
function getPosfromAngle(a) {
/// get angle from circle and add delta
var angle = Math.atan2(delta - yCenter, radius) + a * Math.PI / 180;
return [xCenter + radius * Math.cos(angle),
yCenter + radius * Math.sin(angle)];
}
radius is used as a pseudo position. You can replace this with an actual X position but is frankly not needed.
In this demo, to keep it simple, I have only attached mouse move. Move the mouse over the canvas to see the effect.
As this is demo code it's not structured optimal (separate render of background and the circles etc.).
Feel free to adopt and modify to suit your needs.
This code I have used to achieve this functionality and it works perfect as per your requirement...
public class YourActivity extends Activity {
private class ButtonInfo {
public Button btnObj;
public PointF OrigPos;
public double origAngle;
public double currentAngle;
public double minAngle;
public double maxAngle;
boolean isOnClick = false;
}
private int height;
private double radius;
private PointF centerPoint;
private final int NUM_BUTTONS = 4;
private final int FIRST_INDEX = 0;
private final int SECOND_INDEX = 1;
private final int THIRD_INDEX = 2;
private final int FORTH_INDEX = 3;
private final String FIRST_TAG = "FiRST_BUTTON";
private final String SECOND_TAG = "SECOND_BUTTON";
private final String THIRD_TAG = "THIRD_BUTTON";
private final String FORTH_TAG = "FORTH_BUTTON";
private boolean animInProgress = false;
private int currentButton = -1;
private ButtonInfo[] buttonInfoArray = new ButtonInfo[NUM_BUTTONS];
private int curveImageResource = -1;
private RelativeLayout parentContainer;
private int slop;
private boolean initFlag = false;
private int touchDownY = -1;
private int touchDownX = -1;
private int animCount;
private Context context;
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.fadeinleft, R.anim.fadeoutleft);
// hide action bar in view
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
Thread.setDefaultUncaughtExceptionHandler(
new MyDefaultExceptionHandler(this, getLocalClassName()));
setContentView(R.layout.your_layout);
context = this;
final ImageView curve_image = (ImageView) findViewById(R.id.imageView1);
parentContainer = (RelativeLayout) findViewById(R.id.llView);
// Set buttons on their location
for (int i = 0; i < NUM_BUTTONS; i++) {
buttonInfoArray[i] = new ButtonInfo();
}
Button img1 = (Button) findViewById(R.id.button_option1);
Button img2 = (Button) findViewById(R.id.button_option2);
Button img3 = (Button) findViewById(R.id.button_option3);
Button img4 = (Button) findViewById(R.id.button_option4);
//1st button
buttonInfoArray[FIRST_INDEX].btnObj = (Button) this
.findViewById(R.id.setting_button_option);
buttonInfoArray[FIRST_INDEX].btnObj.setTag(FIRST_TAG);
// 2nd button
buttonInfoArray[SECOND_INDEX].btnObj = (Button) this
.findViewById(R.id.scanning_button_option);
buttonInfoArray[SECOND_INDEX].btnObj.setTag(SECOND_TAG);
// 3rd button
buttonInfoArray[THIRD_INDEX].btnObj = (Button) this
.findViewById(R.id.manual_button_option);
buttonInfoArray[THIRD_INDEX].btnObj.setTag(THIRD_TAG);
// 4th button
buttonInfoArray[FORTH_INDEX].btnObj = (Button) this
.findViewById(R.id.logout_button_option);
buttonInfoArray[FORTH_INDEX].btnObj.setTag(FORTH_TAG);
for (ButtonInfo currentButtonInfo : buttonInfoArray) {
currentButtonInfo.btnObj.setClickable(false);
}
for (ButtonInfo currentButtonInfo : buttonInfoArray) {
currentButtonInfo.btnObj.bringToFront();
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
ViewTreeObserver vtoLayout = parentContainer.getViewTreeObserver();
vtoLayout.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (initFlag == true)
return;
centerPoint = new PointF(0, (parentContainer.getHeight()) / 2);
curve_image.setImageResource(curveImageResource);
ViewTreeObserver vtoCurveImage = curve_image
.getViewTreeObserver();
vtoCurveImage
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (initFlag == true)
return;
ViewConfiguration vc = ViewConfiguration.get(parentContainer
.getContext());
slop = vc.getScaledTouchSlop();
parentContainer.setOnTouchListener(tlobj);
height = curve_image.getMeasuredHeight();
curve_image.getMeasuredWidth();
radius = (height / 2);
double angleDiff = Math.PI / (NUM_BUTTONS + 1);
double initialAngle = (Math.PI / 2 - angleDiff);
for (ButtonInfo currentButtonInfo : buttonInfoArray) {
currentButtonInfo.origAngle = initialAngle;
initialAngle -= angleDiff;
}
double tempCurrentAngle;
double maxAngle = (-1 * Math.PI / 2);
tempCurrentAngle = maxAngle;
for (int i = NUM_BUTTONS - 1; i >= 0; i--) {
buttonInfoArray[i].maxAngle = tempCurrentAngle;
int buttonHeight = buttonInfoArray[i].btnObj
.getHeight();
if (buttonHeight < 30) {
buttonHeight = 80;
}
tempCurrentAngle = findNextMaxAngle(
tempCurrentAngle,
(buttonHeight + 5));
}
double minAngle = (Math.PI / 2);
tempCurrentAngle = minAngle;
for (int i = 0; i < NUM_BUTTONS; i++) {
buttonInfoArray[i].minAngle = tempCurrentAngle;
int buttonHeight = buttonInfoArray[i].btnObj
.getHeight();
if (buttonHeight < 30) {
buttonHeight = 80;
}
tempCurrentAngle = findNextMinAngle(
tempCurrentAngle, (buttonHeight + 5));
}
for (ButtonInfo currentButtonInfo : buttonInfoArray) {
PointF newPos = getPointByAngle(currentButtonInfo.origAngle);
currentButtonInfo.OrigPos = newPos;
currentButtonInfo.currentAngle = currentButtonInfo.origAngle;
setTranslationX(
currentButtonInfo.btnObj,
(int) currentButtonInfo.OrigPos.x - 50);
setTranslationY(
currentButtonInfo.btnObj,
(int) currentButtonInfo.OrigPos.y - 50);
currentButtonInfo.btnObj.requestLayout();
}
initFlag = true;
}
});
}
});
}
/**
* Find next max angle
* #param inputAngle
* #param yDist
* #return
*/
private double findNextMaxAngle(double inputAngle, int yDist) {
float initYPos = (float) (centerPoint.y - (Math.sin(inputAngle) * radius));
float finalYPos = initYPos - yDist;
float finalXPos = getXPos(finalYPos);
double newAngle = getNewAngle(new PointF(finalXPos, finalYPos));
return newAngle;
}
/**
* Find next min angle
* #param inputAngle
* #param yDist
* #return
*/
private double findNextMinAngle(double inputAngle, int yDist) {
float initYPos = (int) (centerPoint.y - (Math.sin(inputAngle) * radius));
float finalYPos = initYPos + yDist;
float finalXPos = getXPos(finalYPos);
double newAngle = getNewAngle(new PointF(finalXPos, finalYPos));
return newAngle;
}
/**
* Apply reset transformation when user release touch
* #param buttonInfoObj
*/
public void applyResetAnimation(final ButtonInfo buttonInfoObj) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); // values from 0
// to 1
animator.setDuration(1000); // 5 seconds duration from 0 to 1
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float) (animation.getAnimatedValue()))
.floatValue();
// Set translation of your view here. Position can be calculated
// out of value. This code should move the view in a half
// circle.
double effectiveAngle = buttonInfoObj.origAngle
+ ((buttonInfoObj.currentAngle - buttonInfoObj.origAngle) * (1.0 - value));
PointF newPos = getPointByAngle(effectiveAngle);
setTranslationX(buttonInfoObj.btnObj, newPos.x - 50);
setTranslationY(buttonInfoObj.btnObj, newPos.y - 50);
}
});
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
animCount++;
if (animCount == NUM_BUTTONS) {
animCount = 0;
currentButton = -1;
animInProgress = false;
for (ButtonInfo currentButtonInfo : buttonInfoArray) {
setTranslationX(currentButtonInfo.btnObj,
currentButtonInfo.OrigPos.x - 50);
setTranslationY(currentButtonInfo.btnObj,
currentButtonInfo.OrigPos.y - 50);
currentButtonInfo.isOnClick = false;
currentButtonInfo.currentAngle = currentButtonInfo.origAngle;
currentButtonInfo.btnObj.setPressed(false);
currentButtonInfo.btnObj.requestLayout();
}
}
}
});
animator.start();
}
/**
* On Touch start animation
*/
private OnTouchListener tlobj = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent motionEvent) {
switch (MotionEventCompat.getActionMasked(motionEvent)) {
case MotionEvent.ACTION_MOVE:
if (currentButton < 0) {
return false;
}
if (animInProgress == true) {
return true;
}
float delta_y = motionEvent.getRawY() - touchDownY;
float delta_x = motionEvent.getRawX() - touchDownX;
updateButtonPos(new PointF((int) delta_x, (int) delta_y));
if (Math.abs(delta_x) > slop || Math.abs(delta_y) > slop) {
buttonInfoArray[currentButton].isOnClick = false;
parentContainer.requestDisallowInterceptTouchEvent(true);
}
return true;
case MotionEvent.ACTION_UP:
animCount = 0;
if (currentButton < 0) {
return false;
}
if(animInProgress == true) {
return true;
}
animInProgress = true;
for (ButtonInfo currentButtonInfo : buttonInfoArray) {
applyResetAnimation(currentButtonInfo);
if (currentButtonInfo.isOnClick) {
// TODO onClick code
String currentTag = (String) currentButtonInfo.btnObj.getTag();
if(currentTag.equalsIgnoreCase(FIRST_TAG)) {
//handle first button click
} else if(currentTag.equalsIgnoreCase(SECOND_TAG)) {
//handle second button click
} else if(currentTag.equalsIgnoreCase(THIRD_TAG)) {
//handle third button click
} else if(currentTag.equalsIgnoreCase(FORTH_TAG)) {
//handle forth button click
}
}
}
return true;
case MotionEvent.ACTION_DOWN:
if (currentButton >= 0) {
return false;
}
if (animInProgress == true) {
return true;
}
animCount = 0;
int buttonIndex = 0;
for (buttonIndex = 0; buttonIndex < NUM_BUTTONS; buttonIndex++) {
final ButtonInfo currentButtonInfo = buttonInfoArray[buttonIndex];
if (isRectHit(currentButtonInfo.btnObj, motionEvent,
currentButtonInfo.OrigPos)) {
currentButton = buttonIndex;
touchDownX = (int) motionEvent.getRawX();
touchDownY = (int) motionEvent.getRawY();
currentButtonInfo.isOnClick = true;
currentButtonInfo.btnObj.setPressed(true);
break;
}
}
if (buttonIndex == NUM_BUTTONS) {
currentButton = -1;
}
break;
default:
break;
}
return false;
}
};
/**
* Get X POS
* #param yPos
* #return
*/
public float getXPos(float yPos) {
float xPos = (float) (centerPoint.x
+ Math.sqrt((radius * radius)
- ((yPos - centerPoint.y) * (yPos - centerPoint.y))));
return xPos;
}
/**
* Get YPos based on X
* #param xPos
* #param isPositive
* #return
*/
public float getYPos(float xPos, boolean isPositive) {
if (isPositive)
return (float) (centerPoint.y - Math.sqrt((radius * radius)
- ((xPos - centerPoint.x) * (xPos - centerPoint.x))));
else
return (float) (centerPoint.y + Math.sqrt((radius * radius)
- ((xPos - centerPoint.x) * (xPos - centerPoint.x))));
}
/**
* Get New angle from define point
* #param newPoint
* #return
*/
private double getNewAngle(PointF newPoint) {
double deltaY = newPoint.y - centerPoint.y;
double deltaX = newPoint.x - centerPoint.x;
double newPointAngle = Math.atan(-1.0 * deltaY / deltaX);
return newPointAngle;
}
/**
* get Point By Angle
* #param angle
* #return
*/
private PointF getPointByAngle(double angle) {
PointF newPos;
double newX = centerPoint.x + Math.cos(angle) * radius;
double newY = (centerPoint.y) - (Math.sin(angle) * radius);
newPos = new PointF((int) newX, (int) newY);
return newPos;
}
/**
* Set new location for passed button
* #param currentButtonIndex
* #param effectiveDelta
* #param percentageCompleted
* #return
*/
private double updateControl(int currentButtonIndex, PointF effectiveDelta,
double percentageCompleted) {
PointF newPos = new PointF();
StringBuilder s1 = new StringBuilder();
double maxAngleForCurrentButton = buttonInfoArray[currentButtonIndex].maxAngle;
double minAngleForCurrentButton = buttonInfoArray[currentButtonIndex].minAngle;
double targetAngleForCurrentButton;
if (effectiveDelta.y > 0) {
targetAngleForCurrentButton = maxAngleForCurrentButton;
} else {
targetAngleForCurrentButton = minAngleForCurrentButton;
}
if (percentageCompleted == -1) {
boolean isYDisplacement = effectiveDelta.y > effectiveDelta.x ? true
: false;
isYDisplacement = true;
if (isYDisplacement) {
float newY = buttonInfoArray[currentButtonIndex].OrigPos.y
+ effectiveDelta.y;
if (newY > (centerPoint.y) + (int) radius) {
newY = (centerPoint.y) + (int) radius;
} else if (newY < (centerPoint.y) - (int) radius) {
newY = (centerPoint.y) - (int) radius;
}
float newX = getXPos(newY);
newPos = new PointF(newX, newY);
s1.append("isYDisplacement true : ");
}
} else {
double effectiveAngle = buttonInfoArray[currentButtonIndex].origAngle
+ ((targetAngleForCurrentButton - buttonInfoArray[currentButtonIndex].origAngle) * percentageCompleted);
newPos = getPointByAngle(effectiveAngle);
s1.append("percentage completed : " + percentageCompleted + " : "
+ effectiveAngle);
}
double newAngle = getNewAngle(newPos);
// For angle, reverse condition, because in 1st quarter, it is +ve, in
// 4th quarter, it is -ve.
if (newAngle < maxAngleForCurrentButton) {
newAngle = maxAngleForCurrentButton;
newPos = getPointByAngle(newAngle);
s1.append("max angle : " + newAngle);
}
if (newAngle > minAngleForCurrentButton) {
newAngle = minAngleForCurrentButton;
newPos = getPointByAngle(newAngle);
s1.append("min angle : " + newAngle);
}
setTranslationX(buttonInfoArray[currentButtonIndex].btnObj,
newPos.x - 50);
setTranslationY(buttonInfoArray[currentButtonIndex].btnObj,
newPos.y - 50);
return newAngle;
}
/**
* Set button Position
* #param deltaPoint
*/
public void updateButtonPos(PointF deltaPoint) {
for (int buttonIndex = 0; buttonIndex < NUM_BUTTONS; buttonIndex++) {
if (currentButton == buttonIndex) {
buttonInfoArray[buttonIndex].currentAngle = updateControl(
buttonIndex, deltaPoint, -1);
double targetAngleForCurrentButton;
if (deltaPoint.y > 0) {
targetAngleForCurrentButton = buttonInfoArray[buttonIndex].maxAngle;
} else {
targetAngleForCurrentButton = buttonInfoArray[buttonIndex].minAngle;
}
double percentageCompleted = (1.0 * (buttonInfoArray[buttonIndex].currentAngle - buttonInfoArray[buttonIndex].origAngle))
/ (targetAngleForCurrentButton - buttonInfoArray[buttonIndex].origAngle);
for (int innerButtonIndex = 0; innerButtonIndex < NUM_BUTTONS; innerButtonIndex++) {
if (innerButtonIndex == buttonIndex)
continue;
buttonInfoArray[innerButtonIndex].currentAngle = updateControl(
innerButtonIndex, deltaPoint, percentageCompleted);
}
break;
}
}
}
/**
* Find whether touch in button's rectanlge or not
* #param v
* #param rect
*/
private static void getHitRect(View v, Rect rect) {
rect.left = (int) com.nineoldandroids.view.ViewHelper.getX(v);
rect.top = (int) com.nineoldandroids.view.ViewHelper.getY(v);
rect.right = rect.left + v.getWidth();
rect.bottom = rect.top + v.getHeight();
}
private boolean isRectHit(View viewObj, MotionEvent motionEvent,
PointF viewOrigPos) {
Rect outRect = new Rect();
int x = (int) motionEvent.getX();
int y = (int) motionEvent.getY();
getHitRect(viewObj, outRect);
if (outRect.contains(x, y)) {
return true;
} else {
return false;
}
}
/**
* On Finish update transition
*/
#Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.activityfinishin, R.anim.activityfinishout);
}
/**
* On Native Back Pressed
*/
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}

Categories

Resources