【问题标题】:ItemSelected event not firing on ListView that's being populated using ListView Adapter - Xamarin AndroidItemSelected 事件未在使用 ListView 适配器填充的 ListView 上触发 - Xamarin Android
【发布时间】:2017-08-29 19:04:38
【问题描述】:

扩展标题:

  • 我在 xamarin for Android 中使用 Adapter 来填充 ListView
  • 我还使用了RecyclerView,它是填充ListView 的一行,其中每一行都包含一个 Imageview 和 2x Textview
  • 当我单击一行中的任意位置时,我需要在 Activity 本身内触发 ItemSelected 事件,以便我可以执行类似于

    的操作
    mListView.ItemSelected += (sender, e) =>
    {
       System.Diagnostics.Debug.Write("LISTVIEW CLICKED AT  : " + e.Position);      
       //Once I know the row position, I can do the rest.
    };
    
  • 我使用的答案:

    Not able to fire the ListVIew Item click event when there is UI Control like Button in xamarin - 推荐设置:

    "Focusable = false" //on any element in the row
    

    虽然在这个答案Unable to get listView.ItemClick to be called in MonoDroid 中,答案的简短引用表明

“问题是我在 ListView 中的 ImageButton。它 正在窃取焦点并消耗触摸事件。”

LilMoke 的解决方案是为他希望触发ItemSelected 事件的行中的每个元素设置以下属性:

imageButton.Focusable = false;
imageButton.FocusableInTouchMode = false;
imageButton.Clickable = true;

imageButton.Click += (sender, args) => Console.WriteLine("ImageButton {0} clicked", position);

看起来不错,这导致了后来的建议:

android:descendantFocusability="blocksDescendants" 

在您的 LinearLayout 中,假设您的 RecycleView 的根元素是 LinearLayout

因为 Android 不允许任何项目在 ListView 中成为焦点。防止行项目获得焦点的更简洁版本。问题是在实现这个之后,我仍然无法触发 ItemSelected 事件。

我尝试过的混乱解决方法:

1) 我尝试从RecyclerView 模板的声明中的每个元素中获取和设置FocusableClickable 属性,但这很混乱,并且不允许我访问 StartActivity 函数,该函数是直接继承的任何类的一部分从活动。 因为这是一个适配器,所以它继承自 BaseAdapter。

我之前的问题都被鄙视了,但是看了this之后,我正在尝试改变我的邪恶方式。

这是与我所描述的内容相关的代码,应该更好地说明情况:

row.axml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="60dp"
    android:weightSum="100"
    android:descendantFocusability="blocksDescendants"
    android:background="#F1F1F1">
    <ImageView
        android:id="@+id/imgPic"
        android:src="@drawable/ic_action_person"
        android:layout_width="0dp"
        android:layout_weight="25"
        android:layout_height="wrap_content"
        android:background="#3B5998"
        android:adjustViewBounds="true"
        android:scaleType="centerCrop" />
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="65">
        <TextView
            android:id="@+id/txtName"
            android:text="Contact Name"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="65"
            android:gravity="center"
            android:textSize="18sp"
            android:textColor="#000" />
        <TextView
            android:id="@+id/txtNumber"
            android:text="(555)-444-2222"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="65"
            android:gravity="center"
            android:textSize="18sp"
            android:textColor="#000" />
    </LinearLayout>
    <ImageButton
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:src="@drawable/xiconsmall"
        android:id="@+id/btnDeleteAlbum" />
</LinearLayout>

listContacts.axml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:minWidth="25px"
        android:minHeight="25px">
        <ListView
            android:layout_width="match_parent"
            android:layout_height="500dp"
            android:visibility="visible"
            android:id="@+id/listView"
            android:fastScrollAlwaysVisible="true"
            android:fastScrollEnabled="true"
            android:smoothScrollbar="true"
            android:clickable="true" />
        <Button
        android:text="Button"
        android:visibility="visible"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnAdd" />
    </LinearLayout>
</ScrollView>

ListContacts.cs

namespace MainMobileDevProject
{
    [Activity(Label = "Display row items", MainLauncher = true, Icon = "@drawable/xs")]    
    //public class ListContacts: AppCompatActivity
    public class ListContacts : Activity
    {
        private ListView mListView;
        private BaseAdapter<Contact> mAdapter;
        private List<Contact> mContacts;
        private ImageView mSelectedPic;        
        private Button mBtnAddPics, button;
        public List<byte[]> imagesByteList = new List<byte[]>();
        public List<string> imagesTagsList = new List<string>();
        Bitmap myBitmap;


        private ImageView _imageView;

        public static class App
        {
            public static File _file;
            public static File _dir;
            public static Bitmap bitmap;
        }

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.listContacts);

            string text = Intent.GetStringExtra("MyData") ?? "Data not available";
            System.Diagnostics.Debug.Write("DATA WE PASSED HERE IS : " + text);

            mListView = FindViewById<ListView>(Resource.Id.listView);
            mContacts = new List<Contact>();
            mBtnAddPics = FindViewById<Button>(Resource.Id.btnAdd);

            //mAdapter = new ContactListAdapter(this, Resource.Layout.pager_item, mContacts, action);
            mAdapter = new ContactListAdapter(this, Resource.Layout.row_contact, mContacts);




            //having an issue with my list adapter
            //none of my event items such as click or itemselected will fire
            //debug log aren't even firing, the proposed solution is to set each row's child elements to have a value of
            //false for "focusable", and a better solution again is to set the root of the row's layout to have 
            // an attribute of android:descendantFocusability="blocksDescendants". this can be seen in row_contact.axml
            //the problem though is that now the row's contents can be accessed, but not from this module, but only in the adapter, which makes things messy of course, but I haven't been able to start a new activity
            //    due to my adapter(ContactListAdapter.cs) inheriting from the BaseAdapter class


            mListView.ItemClick += lv_ItemClick;

            void lv_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
            {
                //not firing
                System.Diagnostics.Debug.Write("LISTVIEW CLICKED AT  : " + e.Position);
            }


                mListView.Adapter = mAdapter;
                //mListView.NotifyDataSetChanged();


            mListView.ItemSelected += (sender, e) =>
            {
                System.Diagnostics.Debug.Write("LISTVIEW CLICKED AT  : " + e.Position);
            };

            mListView.ItemClick += (sender, e) =>
            {
                System.Diagnostics.Debug.Write("LISTVIEW CLICKED AT  : " + e.Position);
            };

            mAdapter.NotifyDataSetChanged();

        }

        private void MListView_ItemClick(object sender, ItemClickEventArgs e)
        {
            System.Diagnostics.Debug.Write("myBitmap row id : " + e.Id);
            System.Diagnostics.Debug.Write("myBitmap row parent is : " + e.Parent);
            System.Diagnostics.Debug.Write("myBitmap row position : " + e.Position);
            System.Diagnostics.Debug.Write("myBitmap row view : " + e.View);
        }



        private void MBtnAddPics_Click(object sender, EventArgs e)
        {
            MySqlConnection con = new MySqlConnection("Server=someawsconstring.something.eu-west-1.rds.amazonaws.com;Port=3307;database=ichangedthis;User Id=ichangedthis;Password=ichangedthis;charset=utf8");
            //List<string> imageslist = new List<string>();
            //List<byte[]> imageslist = new List<byte[]>();

            try
            {
                if (con.State == ConnectionState.Closed)
                {
                    con.Open();
                    System.Diagnostics.Debug.Write("CONN OPEN");
                    //string query = "SELECT * FROM tblUserAlbumPhoto HAVING UserID = '" + Intent.GetStringExtra("UsersID") + "' AND AlbumID = '" + Intent.GetStringExtra("UsersID") +"'";                    
                    string query = "SELECT * FROM tblUserAlbumPhoto HAVING UserID = '" + 1 + "' AND AlbumID = '" + 1 +"'";
                    MySqlCommand cmd = new MySqlCommand(query, con);

                    System.Diagnostics.Debug.Write("TRYING TO READ");
                    MySqlDataReader dataReader = cmd.ExecuteReader();

                    while (dataReader.Read())
                    {                        
                        imagesByteList.Add(Convert.FromBase64String(dataReader.GetString(4)));
                        imagesTagsList.Add(dataReader.GetString(5));
                        //System.Diagnostics.Debug.Write("image still as base64 string is : " + dataReader.GetString(1));
                        //System.Diagnostics.Debug.Write("image converted from base64 found is : " + Convert.FromBase64String(dataReader.GetString(1)));
                    }
                    System.Diagnostics.Debug.Write("image ID found is : " + imagesByteList[0]);
                    System.Diagnostics.Debug.Write("image ID found is : " + imagesByteList[1]);
                }

            }
            catch (MySqlException ex)
            {

            }
            finally
            {
                con.Close();
                System.Diagnostics.Debug.Write("CONN CLOSED");
                System.Diagnostics.Debug.Write("image ID count found is : " + imagesByteList.Count);
            }

            CountImagesList();
            if (imagesByteList.Count > 0) //just a test
            {
                CreateImgFromBytes(imagesByteList, imagesTagsList);
            }
        }

        private void CreateImgFromBytes(List<byte[]> imageslist, List<string> tags)
        {
            //foreach(byte[] ba in imageslist)
            for(int y=0;y<imagesByteList.Count;y++)
            {
                mContacts.Add(new Contact() { Name = tags[y], Image = imageslist[y] });
            }
            //mContacts.Add(new Contact() { Name = "abc", Number = "def", Image = imageslist[0] });
            System.Diagnostics.Debug.Write("imageslist lentth is: " + imageslist.Count);
            mAdapter.NotifyDataSetChanged();

            //Decode with InJustDecodeBounds = true to check dimensions
            //Stream stream = ContentResolver.OpenInputStream(data);
            //BitmapFactory.Options options = new BitmapFactory.Options();
            //options.InJustDecodeBounds = true;
            //BitmapFactory.DecodeStream(stream);

            ////Calculate InSamplesize
            //options.InSampleSize = CalculateInSampleSize(options, requestedWidth, requestedHeight);

            ////Decode bitmap with InSampleSize set
            //stream = ContentResolver.OpenInputStream(data); //Must read again
            //options.InJustDecodeBounds = false;
            //Bitmap bitmap = BitmapFactory.DecodeStream(stream, null, options);
            //return bitmap;
        }

        public void CountImagesList()
        {
            System.Diagnostics.Debug.Write("imageslist lentth is: " + imagesByteList.Count);
        }

        private void PicSelected(ImageView selectedPic)
        {
            mSelectedPic = selectedPic;
            Intent intent = new Intent();
            intent.SetType("image/*");
            intent.SetAction(Intent.ActionGetContent);
            this.StartActivityForResult(Intent.CreateChooser(intent, "Selecte a Photo"), 0);
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);

            //if (resultCode == Result.Ok)
            //{
            //    Stream stream = ContentResolver.OpenInputStream(data.Data);
            //    mSelectedPic.SetImageBitmap(DecodeBitmapFromStream(data.Data, 150, 150));
            //}

            Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);

            Uri contentUri = Uri.FromFile(App._file);
            mediaScanIntent.SetData(contentUri);
            SendBroadcast(mediaScanIntent);

            // Display in ImageView. We will resize the bitmap to fit the display.
            // Loading the full sized image will consume to much memory
            // and cause the application to crash.

            int height = Resources.DisplayMetrics.HeightPixels;
            int width = _imageView.Height;
            App.bitmap = App._file.Path.LoadAndResizeBitmap(width, height);
            System.Diagnostics.Debug.Write("APP . BITMAP IS :: " + App.bitmap);
            if (App.bitmap != null)
            {
                _imageView.SetImageBitmap(App.bitmap);
                myBitmap = App.bitmap;
                //App.bitmap = null;
            }

            // Dispose of the Java side bitmap.
            GC.Collect();
        }

        private void CreateDirectoryForPictures()
        {
            App._dir = new File(
                Environment.GetExternalStoragePublicDirectory(
                    Environment.DirectoryPictures), "Base64Attempt");
            if (!App._dir.Exists())
            {
                App._dir.Mkdirs();
            }
        }

        private bool IsThereAnAppToTakePictures()
        {
            Intent intent = new Intent(MediaStore.ActionImageCapture);
            IList<ResolveInfo> availableActivities =
                PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
            return availableActivities != null && availableActivities.Count > 0;
        }

        private void TakeAPicture(object sender, EventArgs eventArgs)
        {
            Intent intent = new Intent(MediaStore.ActionImageCapture);
            App._file = new File(App._dir, String.Format("myPhoto_{0}.jpg", Guid.NewGuid()));
            intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(App._file));
            StartActivityForResult(intent, 0);
        }

        private Bitmap DecodeBitmapFromStream(Android.Net.Uri data, int requestedWidth, int requestedHeight)
        {
            //Decode with InJustDecodeBounds = true to check dimensions
            System.IO.Stream stream = ContentResolver.OpenInputStream(data);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.InJustDecodeBounds = true;
            BitmapFactory.DecodeStream(stream);

            //Calculate InSamplesize
            options.InSampleSize = CalculateInSampleSize(options, requestedWidth, requestedHeight);

            //Decode bitmap with InSampleSize set
            stream = ContentResolver.OpenInputStream(data); //Must read again
            options.InJustDecodeBounds = false;
            Bitmap bitmap = BitmapFactory.DecodeStream(stream, null, options);
            return bitmap;
        }

        private int CalculateInSampleSize(BitmapFactory.Options options, int requestedWidth, int requestedHeight)
        {
            //Raw height and widht of image
            int height = options.OutHeight;
            int width = options.OutWidth;
            int inSampleSize = 1;

            if (height > requestedHeight || width > requestedWidth)
            {
                //the image is bigger than we want it to be
                int halfHeight = height / 2;
                int halfWidth = width / 2;

                while ((halfHeight / inSampleSize) > requestedHeight && (halfWidth / inSampleSize) > requestedWidth)
                {
                    inSampleSize *= 2;
                }

            }

            return inSampleSize;
        }

        public override bool OnCreateOptionsMenu(IMenu menu)
        {
            MenuInflater.Inflate(Resource.Menu.actionbar_home, menu);
            return base.OnCreateOptionsMenu(menu);
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            switch (item.ItemId)
            {
                case Resource.Id.add:

                    CreateContactDialog dialog = new CreateContactDialog();
                    FragmentTransaction transaction = FragmentManager.BeginTransaction();

                    //Subscribe to event
                    dialog.OnCreateContact += dialog_OnCreateContact;
                    dialog.Show(transaction, "create contact");
                    return true;

                default:
                    return base.OnOptionsItemSelected(item);
            }

        }

        void dialog_OnCreateContact(object sender, CreateContactEventArgs e)
        {
            mContacts.Add(new Contact() { Name = e.Name, Number = e.Number });
            mAdapter.NotifyDataSetChanged();
        }
    }

    /*
    public static class BitmapHelpers
    {
        public static Bitmap LoadAndResizeBitmap(this string fileName, int width, int height)
        {
            // First we get the the dimensions of the file on disk
            BitmapFactory.Options options = new BitmapFactory.Options { InJustDecodeBounds = true };
            BitmapFactory.DecodeFile(fileName, options);

            // Next we calculate the ratio that we need to resize the image by
            // in order to fit the requested dimensions.
            int outHeight = options.OutHeight;
            int outWidth = options.OutWidth;
            int inSampleSize = 1;

            if (outHeight > height || outWidth > width)
            {
                inSampleSize = outWidth > outHeight
                                   ? outHeight / height
                                   : outWidth / width;
            }

            // Now we will load the image and have BitmapFactory resize it for us.
            options.InSampleSize = inSampleSize;
            options.InJustDecodeBounds = false;
            Bitmap resizedBitmap = BitmapFactory.DecodeFile(fileName, options);

            return resizedBitmap;
        }
    }
    */
}

ContactListAdapter.cs

namespace MainMobileDevProject
{
    class ContactListAdapter : BaseAdapter<Contact>
    {
        private Context mContext;
        private int mLayout;
        private List<Contact> mContacts;
        private Action<ImageView> mActionPicSelected;

        public ContactListAdapter(Context context, int layout, List<Contact> contacts, Action<ImageView> picSelected)
        {
            mContext = context;
            mLayout = layout;
            mContacts = contacts;
            mActionPicSelected = picSelected;
        }

        public ContactListAdapter(Context context, int layout, List<Contact> contacts)
        {
            mContext = context;
            mLayout = layout;
            mContacts = contacts;

        }

        public override Contact this[int position]
        {
            get { return mContacts[position]; }
        }

        public override int Count
        {
            get { return mContacts.Count; }
        }

        public override long GetItemId(int position)
        {
            return position;
        }

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            View row = convertView;
            //row.notif
            if (row == null)
            {
                row = LayoutInflater.From(mContext).Inflate(mLayout, parent, false);
            }

            row.FindViewById<TextView>(Resource.Id.txtName).Text = mContacts[position].Name;
            row.FindViewById<TextView>(Resource.Id.txtNumber).Text = mContacts[position].Number;

            ImageView pic = row.FindViewById<ImageView>(Resource.Id.imgPic);
            row.Clickable = true;

            if (mContacts[position].Image != null)
            {
                pic.SetImageBitmap(BitmapFactory.DecodeByteArray(mContacts[position].Image, 0, mContacts[position].Image.Length));
            }

            if (mContacts[position].Image == null)
            {
                //here we should replace mcontacts[position] with imageslist[position], then it'll at least pull
                //the 2 byte[] images we have
                //pic.SetImageBitmap(BitmapFactory.DecodeByteArray(mContacts[position].Image, 0, mContacts[position].Image.Length));
                //pic.SetImageBitmap(BitmapFactory.DecodeByteArray(mContacts[position].Image, 0, mContacts[position].Image.Length));
            }

这是我上面描述的混乱解决方法,无法继续使用它

            //var imageButton = row.FindViewById<ImageView>(Resource.Id.imgPic);
            //imageButton.Focusable = false;
            //imageButton.FocusableInTouchMode = false;
            //imageButton.Clickable = true;

            var removeButton = row.FindViewById<ImageButton>(Resource.Id.btnDeleteAlbum);
            removeButton.Focusable = false;
            removeButton.FocusableInTouchMode = false;
            removeButton.Clickable = true;

            //imageButton.Click += (sender, args) =>
            //{
            //    System.Diagnostics.Debug.Write("myBitmap row id :AAAAAAAAAAAAAAAAAAAA ");
            //    Console.WriteLine("ImageButton {0} clicked", position);

            //};

            removeButton.Click += (sender, args) =>
            {
                System.Diagnostics.Debug.Write("myBitmap row id :AAAAAAAAAAAAAAAAAAAA ");
                Console.WriteLine("RemoveButton {0} clicked", position);

            };



            //pic.Click -= pic_Click;
            pic.Click += (object sender, EventArgs e) =>
            {
                MySqlConnection con = new MySqlConnection("Server=db4free.net;Port=3307;database=ofsligodb;User Id=ofoley1;Password=pinecone;charset=utf8");

                try
                {
                    if (con.State == ConnectionState.Closed)
                    {
                        con.Open();
                        System.Diagnostics.Debug.WriteLine("CONNECTION OPEN*****: ");



                        MySqlCommand Readcmd = new MySqlCommand("DELETE FROM tblUserAlbumPhoto WHERE UserID = '" + 1 + "' AND " +
                            "AlbumID = '" + 1 + "' and ID = '" + position + "'");


                        Readcmd.Connection = con;
                        Readcmd.ExecuteNonQuery();

                        System.Diagnostics.Debug.WriteLine("DELETE SHOULD HAVE HAPPENED*****: ");


                    }

                }

                catch (MySqlException ex)
                {
                    //mTxtImgChoiceInfo.Text = ex.ToString();
                }
                finally
                {
                    con.Close();
                    System.Diagnostics.Debug.WriteLine("CONN CLOSED*****: ");
                }
            };

            return row;
        }



        void pic_Click(object sender, EventArgs e)
        {
            //Picture has been clicked
            System.Diagnostics.Debug.Write("YOU CLICKED ME AND THE ROW INDEX WAS : " + e.position);

        }
    }
}
  • 澄清一下,其中包含一些相关代码,显示此Listview 在单击从 MySql 数据库加载图像的按钮后被填充。
  • 显示了一些用于将加载的位图大小调整为缩略图的代码,也没有问题。 所有与数据库相关的和图像调整大小的代码都可以正常工作,但我觉得无论如何都要包括在内以供其他人理解是很重要的。如果是妨碍大家掌握整个项目的情况,请指教,我会删除。

如有任何歧义/困惑,请告诉我。 如果重复,请解释原因,谢谢。

【问题讨论】:

  • 尝试评论代码row.Clickable = true;,评论后你的代码在我身边运行良好。

标签: c# android listview xamarin.android listadapter


【解决方案1】:

据我了解,您需要在适配器中的每个 View row 上添加 OnClick。

【讨论】:

    猜你喜欢
    • 2016-07-30
    • 2015-11-22
    • 2019-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-02-10
    • 2018-03-10
    • 2016-11-12
    • 1970-01-01
    相关资源
    最近更新 更多