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.
Related
I am getting various unexpected behaviors when trying to implement a snackbar with a custom view in Xamarin forms.
This is the function I am calling (from a dependency service):
public void MySnackBar(string argument, string title, string message, string action_text, string action, MySnackBarPosition position)
{
Activity activity = Xamarin.Essentials.Platform.CurrentActivity; //.Current.Activity;
Android.Views.View view = activity.FindViewById(Android.Resource.Id.Content);
var snack_view = activity.LayoutInflater.Inflate(Droid.Resource.Layout.my_snackbar, (ViewGroup)view);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WrapContent,
FrameLayout.LayoutParams.WrapContent
);
snack_view.LayoutParameters = lp;
var snack_label = snack_view.FindViewById<TextView>(Droid.Resource.Id.my_snack_text);
snack_label.Text = "You're doing something wrong";
snack_label.Tag = $"{argument}:{action}";
snack_label.Click += OnSnackClick;
var snack = Snackbar.Make(activity, snack_view, "", Snackbar.LengthLong);
snack.Show();
}
private void OnSnackClick(object sender, EventArgs args)
{
var v = (View)sender;
var tag = v.Tag;
var data = tag.ToString().Split(':');
//var argument = data[0];
//var action = data[1];
MyApp.App.Instance.HandleSnackbarAction("","");
}
Layout file:
<?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-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="250dp"
android:layout_gravity="end"
android:orientation="horizontal"
android:layout_height="wrap_content"
android:padding="20dp"
android:background="#android:color/transparent">
<TextView
android:id = "#+id/my_snack_text"
android:elevation="8px"
android:background="#fff"
android:layout_marginEnd="20dip"
android:layout_marginTop="20dip"
android:padding = "20dip"
android:gravity="center"
android:text="hello hsdkfhdskjh dsfhsdjkh fjsdkfh sdjkhf dskj hfjkdshfjkdshfjkdsh"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
When I call this code for a first time the TextView displays with the correct text, but there are two big issues:
An empty button appears in the top left of the screen, presumably a snackbar action button
The TextView doesn't disappear when the snackbar times out
The v.Tag inside the OnSnackClick is null
If I call the code a second time, the same issues persist but on this occasion the TextView's text isn't even set and only the default text from the xml file is displayed.
First Call:
Subsequent Calls:
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 :
I have a MvxFragment that is bound to a view model that has two ICommand properties defined. This fragment contains an MvxListView and can be part of different activities/layouts dependant on the device size/orientation.
What I want to know is how to specify the ItemClick event command of the MvxBind property of the MvxListView dynamically or is there a better way to handle this use case? Should I use a separate fragment?
A similar use case to the one I am trying to achieve is within the Overview section of this Xamarin Dev Guide
View
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<MvxListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="ItemsSource Courses; ItemClick Comamnd1"
local:MvxItemTemplate="#layout/coursetemplate" />
</LinearLayout>
ViewModel (Simpified)
public class MyViewModel : MvxViewModel
{
private MvxCommand<MyMessage> messageCommand;
public MyViewModel (IMvxMessenger messenger, IHttpClientBuilderService httpClientBuilder)
{
this.httpClientBuilder = httpClientBuilder;
this.messenger = messenger;
}
public ICommand Comamnd1 {
get { return new MvxCommand<Course> ((c) => ShowViewModel<MyOtherViewModel>(c)); }
}
public ICommand Command2 {
get
{
messageCommand = messageCommand ?? new MvxCommand<MyMessage>(c =>
{
var message = new MyMessage(this, c);
messenger.Publish(message);
});
return selectedCourse;
}
}
}
OK, you can do this my overriding the OnActivityCreated event for the fragment as below.
public override void OnActivityCreated (Bundle savedInstanceState)
{
base.OnActivityCreated (savedInstanceState);
MvxListView list = Activity.FindViewById<MvxListView>(Resource.Id.[theID]);
if (Activity.FindViewById<View>(Resource.Id.[fragmentID]) != null)
list.ItemClick = ((MyViewModel)ViewModel).Command1;
else
list.ItemClick = ((MyViewModel)ViewModel).Command2;
}
You can pull out the list by using the Activity.FindViewById function and then set the appropraite ICommand or IMvxCommand from the ViewModel via the list.ItemClick event
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.
I'm currently trying to program an Android Launcher with Fragments but I have problems with the Views on the Fragments.
I have a Dock-Fragment with a Dock-Controller which allow the user to change fragments, such as apps menu, settings fragment etc. The Dock is displayed on the buttom of the display, the Fragments(apps menu, settings fragment) should be displayed above the Dock.
The problem is, that the apps menu is not shown in its associated Fragment but rather in the Dock Fragment behind the dock icons,... So I guess, the app menu fragment gets the wrong view in its onCreateView()-Method, but I don't get why.
This is the code of the MainActivity that extends from FragmentActivity. I add the fragments to the manager.
private void addDockToManager() {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(dbConnection.getLayout(DOCK_TAG), dockController.getFragment(), DOCK_TAG);
ft.commit();
}
private void addPluginsToManager() {
FragmentManager fm = null;
FragmentTransaction ft = null;
for(String key : controllerMap.keySet()) {
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
FrameController controller = null;
if ((controller = controllerMap.get(key)) != null) {
ft.add(dbConnection.getLayout(key), controller.getFragment(), key);
if (key.equals(standardFrame))
ft.addToBackStack(key);
}
ft.commit();
fm.executePendingTransactions();
}
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
for(String key : controllerMap.keySet()) {
if (controllerMap.get(key) != null && !key.equals(standardFrame)) {
ft.hide(fm.findFragmentByTag(key));
}
}
ft.commit();
}
The layouts are hardcoded at the moment in dbConnection:
public int getLayout(String name) {
int layout = -1;
switch(name) {
case "app_menu" : layout = R.id.fl_app_menu;
case "settings" : layout = R.id.fl_settings;
case "dock" : layout = R.id.fl_dock;
}
return layout;
}
The MainActivity's xml looks like that:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rl_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.activity.MainActivity"
tools:ignore="MergeRootFrame" >
<FrameLayout
android:id="#+id/fl_settings"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_above="#+id/fl_dock"
android:background="#00ffffff" >
</FrameLayout>
<FrameLayout
android:id="#+id/fl_app_menu"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_above="#+id/fl_dock"
android:background="#00ffffff" >
</FrameLayout>
<FrameLayout
android:id="#+id/fl_dock"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" >
</FrameLayout>
</RelativeLayout>
The xml of the apps menu is a gridview and looks like that:
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gv_apps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="6"
android:gravity="center"
android:columnWidth="50dp"
android:stretchMode="columnWidth" >
</GridView>
The App Fragment looks like that:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup group,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.external_apps, group, false);
layout = (GridView) view.findViewById(R.id.gv_apps);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
GridViewAdapter gridViewAdapter = new GridViewAdapter((AppMenuController) myController, apps);
((GridView) layout).setAdapter(gridViewAdapter);
}
And the getView Method of the GridViewAdapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(controller.getMainActivity().getApplicationContext());
imageView.setImageDrawable(buttons.get(position).getIcon());
imageView.setLayoutParams(new GridView.LayoutParams(65, 65));
return imageView;
}
I hope what I mentioned is enough to resolve the problem. I am searching the web for hours but I found no solution.
It's a much simpler problem than that I think.
public int getLayout(String name) {
int layout = -1;
switch(name) {
case "app_menu" : layout = R.id.fl_app_menu;
case "settings" : layout = R.id.fl_settings;
case "dock" : layout = R.id.fl_dock;
}
return layout;
}
Should be:
public int getLayout(String name) {
int layout = -1;
switch(name) {
case "app_menu" :
layout = R.id.fl_app_menu;
break;
case "settings" :
layout = R.id.fl_settings;
break;
case "dock" :
layout = R.id.fl_dock;
break;
}
return layout;
}
Because switch-case structure is still essentially just an organized goto, and not actually an if-else structure, aka if you don't break out, then all cases will run sequentially.
I found the problem. And it was in a part of the code I never expected it to be.
The Dummy-Switch-Case in dbConnection caused it. Seemingly Strings aren't compared by value but rather by reference in such a Switch-Case. So it always chose the dock container layout to be associated with the app menu in the fragment manager,...