Wrong coordinates when drawing OnTouch lines on ImageView using canvas - imageview

Thank you in advice for your help!
I am trying to make a simple application to draw lines on a ImageView usings OnTouch tap detection and path and bitmap painting
Tell me please where i did a mistake with the coordinates i take un OnTouch
Probably there is some commmon approache how to recalc the taked X Y coordinates
Probably i should better use some kind of different layout or OnTouch listener implementation
The image size - 1024 x 768
Android Screen 1280 x 800
The image is center loaded inside the ImageView
if i do the followings - i am getting correct drawn square with correct borders
void Button3OnClick(object sender, EventArgs eventArgs)
{
OnImageClick(50, 50, 0, 0);
OnImageClick(950, 50, 0, 0);
OnImageClick(950, 710, 0, 0);
OnImageClick(50, 710, 0, 0);
OnImageClick(40, 40, 0, 0);
DrawPolygon();
}
Why OnTouch painting gives wrong result?
Android Screenshot
Red line - the path i draw manually
Green line - the line the app draws finally
My XAML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="#+id/MyButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Задать Изображение" />
<Button
android:id="#+id/MyButton1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Сброс" />
<Button
android:id="#+id/MyButton2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Расчёт" />
<Button
android:id="#+id/MyButton3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="ВЫХОД" />
<ImageView
android:src="#android:drawable/ic_menu_gallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="#+id/imageView1" />
</LinearLayout>
Here is my code
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
metrics = new DisplayMetrics();
WindowManager.DefaultDisplay.GetMetrics(metrics);
widthInDp = metrics.WidthPixels;
heightInDp = metrics.HeightPixels;
SetContentView(Resource.Layout.Main);
pointList = new List<Point>();
_imageView = FindViewById<ImageView>(Resource.Id.imageView1);
_imageView.SetOnTouchListener(this);
button = FindViewById<Button>(Resource.Id.MyButton);
button1 = FindViewById<Button>(Resource.Id.MyButton1);
button2 = FindViewById<Button>(Resource.Id.MyButton2);
button3 = FindViewById<Button>(Resource.Id.MyButton3);
button.Click += ButtonOnClick;
button1.Click += Button1OnClick;
button2.Click += Button2OnClick;
button3.Click += Button3OnClick;
}
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Up:
TimeSpan span = lastTouchDown - System.DateTime.Now;
float UpTouchX = e.GetX();
float UpTouchY = e.GetY();
float differenceX = Math.Abs(initialTouchX - UpTouchX);
float differenceY = Math.Abs(initialTouchY - UpTouchY);
if ((span.TotalMilliseconds < 300) && (differenceY < 10) && (differenceX < 10))
OnImageClick(UpTouchX, UpTouchY, v.MeasuredHeight, v.MeasuredWidth);
break;
case MotionEventActions.Down:
lastTouchDown = System.DateTime.Now;
initialTouchX = e.GetX();
initialTouchY = e.GetY();
break;
}
return true;
}
void OnImageClick(float x, float y, int MeasuredHeight, int MeasuredWidth)
{
int measuredHeight = MeasuredHeight;
int measuredWidth = MeasuredWidth;
Point point = new Point();
point.x = x;
point.y = y;
pointList.Add(point);
button2.Text = string.Format("Рассчёт Вершин {0}", pointList.Count);
DrawPolygon();
}
void DrawPolygon()
{
var paint = new Paint();
paint.SetARGB(255, 200, 255, 0);
paint.SetStyle(Paint.Style.Stroke );
paint.StrokeWidth = 5;
Bitmap tempBitmap = Bitmap.CreateBitmap(baseBitmap.Width, baseBitmap.Height, Bitmap.Config.Rgb565);
Canvas tempCanvas = new Canvas(tempBitmap);
tempCanvas.DrawBitmap(baseBitmap, 0, 0, null);
var path = new Path();
foreach (Point pnt in pointList)
{
if (path.IsEmpty)
path.MoveTo(pnt.x, pnt.y);
else
path.LineTo(pnt.x, pnt.y);
tempCanvas.DrawCircle(pnt.x, pnt.y, 7, paint);
}
if (!path.IsEmpty)
tempCanvas.DrawPath(path, paint);
_imageView.SetImageDrawable (new BitmapDrawable(tempBitmap));
}

Related

Xamarin.Forms. How to add views on top of map, but right on the defined lat/lng position?

Currently i am developing a taxi app. But got to the point where i need to put some views on top of the map that will move when i move me map. I am wondering how it is possible to do it using Xamarin.Forms.GoogleMaps package. The inbuilt "Icon = BitmapDescriptorFactory.FromView()" won't work for me because i need a view that consists of two or more clickable parts + i don't want the views to overlap. Adding a picture of what i need to achieve. Any help would be appreciated
Here is the pic
Are you using Xamarin.Forms.Maps? https://www.nuget.org/packages/Xamarin.Forms.Maps/
This component makes it easier to work as you need.
In xaml file you need add this code:
>
<ContentView Content="{Binding Map}"/>
And in your viewModel add this code:
>
Map = new Map(MapSpan.FromCenterAndRadius(new Position(location.Latitude, location.Longitude), Distance.FromKilometers(1)));
To add pins, add this:
>
var pin = new Pin
{
Type = PinType.Place,
Address = String.Empty,
Label = item.Name,
Position = new Position(item.Geometry.Location.Lat, item.Geometry.Location.Lng),
};
pin.InfoWindowClicked += async (s, args) => { await PinClicked((Pin)s); };
Map.Pins.Add(pin);
Welcome to SO !
You can create a custom renderer for the Map control, which displays a native map with a customized pin and a customized view of the pin data on each platform.
Refer to this document :https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/map-pin#creating-the-custom-map
Android :
Custom the MapPlusInfo.xml in android native can achieve that:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center_vertical"
android:background="#android:color/white"
android:layout_marginRight="20dp">
<TextView
android:id="#+id/InfoWindowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="InfoWindowTitle"
android:textColor="#android:color/black"
android:textStyle="bold" />
<TextView
android:id="#+id/InfoWindowSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="InfoWindowSubtitle"
android:textColor="#android:color/black" />
</LinearLayout>
<ImageButton
android:id="#+id/InfoWindowButton"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/plus" />
</LinearLayout>
The renderer code in Android :
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;
public CustomMapRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
}
}
protected override void OnMapReady(GoogleMap map)
{
base.OnMapReady(map);
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
}
protected override MarkerOptions CreateMarker(Pin pin)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
return marker;
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(Marker marker)
{
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (customPin.Name.Equals("Xamarin"))
{
view = inflater.Inflate(Resource.Layout.MapPlusInfo, null);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
return view;
}
else
{
//view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
}
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
}
The effect :

listview inside scrollview not working on android version lower than API 18

ListView inside ScrollView works perfectly on android API greater than 18 but, with lower than API 18 app crash showing java.lang.NullPointerException in line
listItem.measure(0, 0); on MyUtils.java
MyUtils.java
public static void setListViewHeightBasedOnChildren(ExpandableListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
listView.requestLayout();
}
fragment_list.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="sportsnewsforcrazysportpepllatestnewsjstwaao.com.sportsnews.fragments.Frag_List">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.android.volley.toolbox.NetworkImageView
android:id="#+id/networkImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="#color/divider"
android:minHeight="150dp"/>
<ExpandableListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/expandableListView"
android:scrollbars="none"
android:background="#color/app_background"
android:childDivider="#color/app_background"/>
</LinearLayout>
</ScrollView>
Fragment_list.java
adapter = new ExpandableListViewAdapter(getActivity() , dataList);
expandableListView.setAdapter(adapter);
MyUtils.setListViewHeightBasedOnChildren(expandableListView);
expandableListView.setOnGroupClickListener(this);
expandableListView.setOnChildClickListener(this);
In my app,if listview item layout's root view is RelativeLayout,it will cause this crash.Because in api 18 RelativeLayout source code
if(mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}
and in api >=19 RelativeLayout source code
if(mLayoutParams != null && mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}
the mLayoutParams maybe null if you inflate item view in this method:
contentView = (ViewGroup) LayoutInflater.
rom(context).inflate(R.layout.select_text_oprate_window, null);
So,you can use this method:
contentView = (ViewGroup)
LayoutInflater.from(context).inflate(R.layout.select_text_oprate_window, parent,false);
Also you can change item layout's root view to LinearLayout.

background overlaps my chronometer

I put in a stopwatch png, and I am trying to design it right now but when I place the chronometer I cant see it overtop the image. Color isn't the issue so it isn't blending in. I tried making the background of the chronometer the same as the image but still nothing. I see it once I move it away from image, but not where I want it.
<Chronometer
android:id="#+id/chronometer"
android:format="%s"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:textColor="#ffffff"
android:background="#drawable/stopwatch2"
android:layout_alignTop="#+id/my_button"
android:layout_alignLeft="#+id/my_button"
android:layout_alignStart="#+id/my_button" />
<Button
android:id="#+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/stopwatch2"
android:text=" "
android:layout_marginTop="71dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
MAIN ACTIVITY
public class MainActivity extends AppCompatActivity {
Chronometer mChronometer;
private int loop = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setClick();
}
public void setClick() {
Button button;
mChronometer = (Chronometer) findViewById(R.id.aChronometer);
// Watch for button clicks.
button = (Button) findViewById(R.id.stopWatch);
button.setOnClickListener(mStartListener);
button = (Button) findViewById(R.id.stopWatch);
button.setOnClickListener(mStartListener);
button = (Button) findViewById(R.id.stopWatch);
button.setOnClickListener(mStartListener);
}
View.OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
if (loop == 0) {
mChronometer.setBase(SystemClock.elapsedRealtime());
mChronometer.start();
loop++;
} else if (loop == 1) {
mChronometer.stop();
loop++;
} else if (loop == 2) {
mChronometer.setBase(SystemClock.elapsedRealtime());
loop = 0;
}
}
};

Android ,overlapping imageview android

I tried to display a small imageview above the big imageview .It worked fine.
But when I made the small imageview to appear in rounded shape, It is not showing up.. Your reply will be helpful
there is no error or warning or any crashing of avd .simply the small imageview is not showing
.xml file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:maxHeight="1000dp"
android:maxWidth="1000dp"
android:scaleType="fitXY"
android:src="#drawable/ciaz"
tools:ignore="ContentDescription" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/imageView1"
android:layout_centerHorizontal="true"
android:layout_marginBottom="14dp"
android:adjustViewBounds="true"
android:maxHeight="300dp"
android:maxWidth="300dp"
android:scaleType="fitXY"
android:src="#drawable/ac"
android:visibility="visible"
tools:ignore="ContentDescription,RtlHardcoded" />
</RelativeLayout>
mainactivity.java
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
im1 = (ImageView) findViewById(R.id.imageView1);
im2 = (ImageView) findViewById(R.id.imageView2);
im1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
try
{
int action=arg1.getAction();
float x=(float)arg1.getX();
float y=(float)arg1.getY();
if(action==MotionEvent.ACTION_DOWN)
{
context = getApplicationContext();
duration = Toast.LENGTH_SHORT;
String g= x+" "+y;
Toast toast = Toast.makeText(context, g, duration);
toast.show();
if((x>0.0) && (x<100)&&(y>0.0) && (y<100))
{
im2.setVisibility(View.VISIBLE);
Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.ac);
roundedImage = new RoundedImageView(bm);
im2.setImageDrawable(roundedImage);
}
}
}
catch(Exception e)
{
Toast toast = Toast.makeText(context, "exception", duration);
toast.show();
}
return false;
}
});
}
enter code here
Make sure that if you are using relative layout, place big image view first and then after small age view. Otherwise big image view overlaps small image view. If that is not the case can u please give code snippet to help more and find out the issue ?
You are giving 1000 h and 1000 w for first image view, and using android:layout_alignBottom="#+id/imageView1" in second imageview. So it goes out of ur device screen. Use match parent for first image view and remove alignbottom.

Getting multiple item with one touch in GridView

I have a gridview with custom buttons called bg_button in each cell. I am trying to create a boggle-like game and still a newbie in Android. I was searching through internet about this issue over a week now and still got nothing.
The issue is, when a touch_down I can get the specific item without any problem but when I start to move diagonal, I get multiple grid items that I do not want. For example;
A O F T
K T U L
T R S V
J O K U
The grid that I have above, when I touch T and then trying to move to O, I get;
T -> J -> O or T -> R -> O
I do not want J or R, but still I am touching that as well. I have tried to change to padding, or vertical and horizontal spacing but the issue remained the same. Could you please help me about this issue or at least can you give me a way to do this, or at least a specific tag that I can google and find information that can help me? Thank you so much for your time.
This is the part of my code for the touch event. I am saving the path to an ArrayList and I am sorry for the messy code. I will clean once I finish hardcoding:
final ArrayList<Integer> myList = new ArrayList<Integer>();
gridView = (GridView) this.findViewById(R.id.gridFriends);
MyAdapter gridAdapter = new MyAdapter(Boggler.this,board_1d);
gridView.setAdapter(gridAdapter);
gridView.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
final GridView layout = (GridView)v;
int action = event.getActionMasked();
float currentXPosition = event.getX();
float currentYPosition = event.getY();
int position = gridView.pointToPosition((int) currentXPosition, (int) currentYPosition);
// position = layout.pointToPosition( (int)event.getX(), (int)event.getY() );
while(position == -1)
position=event.getAction();
View v2 =layout.getChildAt(position);
myList.add(position);
Bg_button bt = (Bg_button) v2.findViewById(R.id.grid_item);
bt.setPressed(true);
Log.d(this.getClass().getName(), String.format("Over view.id[%d]", position));
if (action == MotionEvent.ACTION_MOVE){
myList.add(position);
return true;
}
if (action == MotionEvent.ACTION_UP) {
Log.d(this.getClass().getName(), myList.toString());
int i=0,j=0;
int state = 0;
Object[] st = myList.toArray();
for (Object s : st) {
if (myList.indexOf(s) != myList.lastIndexOf(s)) {
myList.remove(myList.lastIndexOf(s));}
else {
v2 =layout.getChildAt(myList.get(myList.lastIndexOf(s)));
bt = (Bg_button) v2.findViewById(R.id.grid_item);
bt.setPressed(false);
name = name + bt.getText();
Log.d(this.getClass().getName(), name);
}
}
And this is the xml files that I am using button_boggler:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.proje_test.bg_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.proje_test.Bg_button
android:id="#+id/grid_item"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:clickable="false"
android:textAppearance="?android:attr/textAppearanceMedium"
android:background="#drawable/color_bg_selector"
android:textSize="50dp"
/>
</LinearLayout>
And activity_boggler:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<GridView
android:id="#+id/gridFriends"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:clipChildren="true"
android:columnWidth="100dp"
android:gravity="center"
android:numColumns="4"
android:scrollbars="none"
android:stretchMode="columnWidth" >
</GridView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="#+id/feedback"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</LinearLayout>
I have found a solution that is working for me. I have seen a couple of same questions about this kind of issues without answers because of that I will answer my own question so maybe it can help those who have this kind of issues. I made custom button class with different attrs called Bg_button:
public class Bg_button extends Button {
private static final int[] STATE_C = {R.attr.state_chosen};
private static final int[] STATE_R = {R.attr.state_right};
private static final int[] STATE_W = {R.attr.state_wrong};
public boolean mIschosen = false;
public boolean mIsright = false;
public boolean mIswrong = false;
public Bg_button(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 3);
if (mIschosen) {
mergeDrawableStates(drawableState, STATE_C);
}
if (mIsright) {
mergeDrawableStates(drawableState, STATE_R);
}
if (mIswrong) {
mergeDrawableStates(drawableState, STATE_W);
}
return drawableState;
}
public void setchosen(boolean ischosen) {mIschosen = ischosen;
refreshDrawableState();}
public void setright(boolean isright) {mIsright = isright;
refreshDrawableState();}
public boolean setwrong(boolean iswrong) {mIswrong = iswrong;
refreshDrawableState();
return true;}
#Override
public void getHitRect(Rect outRect) {
outRect.set(getLeft() + 20, getTop() + 20, getRight() - 20, getBottom() - 20);
}
}
So the solution I have found is limiting the touch area of the button so they do not intercept. I don't know how ethical this is but it is working for me now.
#Override
public void getHitRect(Rect outRect) {
outRect.set(getLeft() + 20, getTop() + 20, getRight() - 20, getBottom() - 20);
}
}
This is the best answer I can come up with so far. I limited the touch area of button to its center with reversing the transaction that we do for expanding it. And the 3 lines code above did the trick. I hope this helps.

Resources