Cách ẩn một mục trong Android Spinner


101

Tôi đang tìm cách ẩn một mục trong tiện ích con quay Android. Điều này sẽ cho phép bạn mô phỏng một vòng quay không có mục nào được chọn và đảm bảo rằng lệnh gọi lại onItemSelected () luôn được gọi cho mọi mục được chọn (nếu mục ẩn là "hiện tại"). Thông thường, luôn có một mục trong spinner không tạo lệnh gọi lại, cụ thể là mục hiện tại.

Có một số mã trên stackoverflow về cách vô hiệu hóa (chuyển sang màu xám) các mục, nhưng không phải cách ẩn các mục hoàn toàn như thể chúng không tồn tại.

Sau nhiều thử nghiệm, tôi đã đưa ra một giải pháp hack-ish có thể hoạt động trên nhiều nền tảng Android cũ và mới khác nhau. Nó có một số nhược điểm thẩm mỹ nhỏ mà khó nhận thấy. Tôi vẫn muốn biết một giải pháp chính thức hơn, ngoài việc "không làm điều đó với máy quay".

Điều này luôn ẩn mục đầu tiên trong spinner, nhưng khá dễ dàng được mở rộng để ẩn một mục tùy ý hoặc nhiều hơn một mục. Thêm một mục giả có chứa một chuỗi trống ở đầu danh sách các mục quay của bạn. Bạn có thể muốn đặt lựa chọn con quay hiện tại thành mục 0 trước khi hộp thoại con quay mở ra, điều này sẽ mô phỏng một con quay không được chọn.

Ví dụ thiết lập Spinner với ghi đè phương thức ArrayAdapter:

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");

// Populate the spinner using a customized ArrayAdapter that hides the first (dummy) entry
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list) {
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent)
    {
        View v = null;

        // If this is the initial dummy entry, make it hidden
        if (position == 0) {
            TextView tv = new TextView(getContext());
            tv.setHeight(0);
            tv.setVisibility(View.GONE);
            v = tv;
        }
        else {
            // Pass convertView as null to prevent reuse of special case views
            v = super.getDropDownView(position, null, parent);
        }

        // Hide scroll bar because it appears sometimes unnecessarily, this does not prevent scrolling 
        parent.setVerticalScrollBarEnabled(false);
        return v;
    }
};

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

bạn đã tìm thấy gì trên các mạng khác? Bạn đã thử những gì cho đến nay?
dldnh

Xin lỗi, tôi không biết phải làm thế nào.
Louisth

Giải pháp tốt! Nhưng tôi nghĩ rằng tv.setVisibility(View.GONE);dòng là không cần thiết. Nhận xét về nó dường như không tạo ra bất kỳ sự khác biệt nào (trực quan), ít nhất là trên Android 4.4.2 / KitKit (trên LG / Google Nexus 4).
Matthias

Câu trả lời trong câu hỏi này hoạt động tốt ..
Rat-a-tat-a-tat Ratatouille

Đây có thể không phải là một cải tiến, nhưng tôi đã sử dụng setTag(1)trên textView ở vị trí 0, sau đó được sử dụng convertView.getTag() != nullđể xác định xem chế độ xem được sử dụng lại là chế độ xem chiều cao 0 được tạo cho vị trí 0 hay chế độ xem bình thường được sử dụng cho các mục spinner khác. Điều này để super.getDropDownView(position, convertView, parent)đôi khi tôi có thể sử dụng thay vì luôn tạo một chế độ xem mới.
Daniel Handojo

Câu trả lời:


49

Để ẩn một mục tùy ý hoặc nhiều hơn một mục, tôi nghĩ rằng bạn có thể triển khai bộ điều hợp của riêng mình và đặt chỉ mục (hoặc danh sách mảng chỉ mục) mà bạn muốn ẩn.

public class CustomAdapter extends ArrayAdapter<String> {

     private int hidingItemIndex;

     public CustomAdapter(Context context, int textViewResourceId, String[] objects, int hidingItemIndex) {
         super(context, textViewResourceId, objects);
         this.hidingItemIndex = hidingItemIndex;
     }

     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
         View v = null;
         if (position == hidingItemIndex) {
             TextView tv = new TextView(getContext());
             tv.setVisibility(View.GONE);
             v = tv;
         } else {
             v = super.getDropDownView(position, null, parent);
         }
         return v;
     }
 }

Và sử dụng bộ điều hợp tùy chỉnh của bạn khi bạn tạo danh sách các mục.

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");

int hidingItemIndex = 0;

CustomAdapter dataAdapter = new CustomAdapter(this, android.R.layout.simple_spinner_item, list, hidingItemIndex);

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

(Tôi đã không kiểm tra mã) hy vọng rằng sẽ giúp.


1
Đây không phải là một giải pháp. Cập nhật cho câu hỏi cung cấp mã chính xác.
dldnh

13
Không có tv.setHeight (0), TextView vẫn hiển thị.
v4r

xin chào, tôi đã sử dụng mã này để ẩn mục đầu tiên trong con quay của mình, nó đang hoạt động tốt, con quay của tôi sẽ hiển thị cho tôi mục thứ hai, nhưng khi tôi nhấp vào mục thứ hai, văn bản trên mục đó sẽ được đặt thành con quay của tôi, tôi không muốn hiển thị bất kỳ văn bản nào trên spinner của tôi khi tôi nhấp vào mục đó, vui lòng hướng dẫn tôi?
Đạt được

1
Tuyệt vời :) Giải pháp đơn giản :)
Chaitanya

1
Hoạt động như một cái duyên ..!
Krupa Kakkad

20

Dễ dàng hơn để ẩn một mục ở cuối danh sách bằng cách cắt bớt danh sách.

Nhưng bạn phải chọn nó trước để nó xuất hiện trong spinner, và sau đó kiểm tra xem lựa chọn đã được thay đổi thành một trong các mục được hiển thị chưa.

List<String> list = new ArrayList<String>();
list.add("string1");
list.add("string2");
list.add("string3");
list.add("[Select one]");
final int listsize = list.size() - 1;

ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, list) {
    @Override
    public int getCount() {
        return(listsize); // Truncate the list
    }
};

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);
mySpinner.setSelection(listsize); // Hidden item to appear in the spinner

3
Tôi đã tìm kiếm trong nửa giờ để cố gắng tìm ra một cách tiếp cận rõ ràng và đây là cách tiếp cận tốt nhất cho đến nay. Chỉ cần cắt ngắn danh sách, nhưng mục thực sự tồn tại. Thông minh.
KSdev

1
Điều này dường như không hoạt động trong Lollipop, kiểm tra [Chọn một] không hiển thị ban đầu trong Spinner. Mã tương tự trên các phiên bản Android cũ hơn dường như làm những gì chúng ta muốn.
Jonathan Caryl

1
Văn bản con quay được thay đổi thành 'String3' khi thay đổi hướng ngay cả khi con quay không bị ảnh hưởng. @Romich
Yksh 17/02/16

bất kỳ ai có thể xem xét câu hỏi của tôi ?
Moeez

5

Để ẩn bất kỳ mục nào trong trình đơn thả xuống của spinner, bạn cần chuyển vị trí của mục cần ẩn dựa trên các tiêu chí bắt buộc. Tôi đã đạt được điều này trong một trường hợp sử dụng là ẩn mục được chọn từ menu thả xuống

public class CustomAdapter extends ArrayAdapter<String> {

private List<String> dates;
private int hideItemPostion;

public CustomAdapter (Context context, int resource, List<String> dates) {
    super(context, resource,dates);
    this.dates=dates;
}
public void setItemToHide(int itemToHide)
{
    this.hideItemPostion =itemToHide;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
    View v = null;
    if (position == hideItemPostion) {
        TextView tv = new TextView(getContext());
        tv.setVisibility(View.GONE);
        tv.setHeight(0);
        v = tv;
        v.setVisibility(View.GONE);
    }
    else
        v = super.getDropDownView(position, null, parent);
    return v;
}}

Và thiết lập bộ điều hợp là một cái gì đó như thế này

final CustomAdapter dataAdapter = new CustomAdapter(this,R.layout.spinner_item,dates);
    dataAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
    spinner.setAdapter(dataAdapter);
    dataAdapter.setItemToHide(0);

Khi chọn một số mục từ menu thả xuống, vị trí cũng cần thay đổi

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, final int i, long l) {
        dataAdapter.notifyDataSetChanged();
            mEPGDateSelector.setSelection(i);
            dataAdapter.setItemToHide(i);}

             @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    });

1

Chỉ vì sự quan tâm, tôi đã đưa ra giải pháp sử dụng "Prompt" như một gợi ý. Mã này được tạo ra cho Xamarin.Android, nhưng nó có thể được chuyển hoàn hảo sang Java trong 10 phút. Sử dụng nó giống như một cách đơn giản ArrayAdaptermà không cần thêm các mục được lập chỉ mục 0 hoặc được lập chỉ mục vào mảng nguồn. Nó cũng được đặt SpinnerGeolocation.SelectedItemIdthành -1 khi không có gì được chọn ( hintlà mục hiện tại).

public class ArrayAdapterWithHint<T>: ArrayAdapter<T>
{
    protected bool HintIsSet = false;
    protected int HintResource = 0;

    public ArrayAdapterWithHint(Context context, int textViewResourceId,
                   T[] objects)
        : base(context, textViewResourceId, objects)
    {
    }
    public ArrayAdapterWithHint(Context context, int hintResource,
                   int textViewResourceId, T[] objects)
        : base(context, textViewResourceId, objects)
    {
        HintResource = hintResource;
    }
    public ArrayAdapterWithHint(Context context, int textViewResourceId,
             IList<T> objects)
        : base(context, textViewResourceId, objects)
    {
    }
    public ArrayAdapterWithHint(Context context, int hintResource,
                    int textViewResourceId, IList<T> objects)
        : base(context, textViewResourceId, objects)
    {
        HintResource = hintResource;
    }

    public override View GetDropDownView(int position, View convertView,
                ViewGroup parent)
    {
        if (HintIsSet)
            return base.GetDropDownView(position + 1,
                               convertView, parent);
        return base.GetDropDownView(position, convertView, parent);
    }

    public override View GetView(int position, View convertView,
                      ViewGroup parent)
    {
        if (!HintIsSet && parent is Spinner && 
                    !string.IsNullOrWhiteSpace((parent as Spinner).Prompt))
        {
            Insert((parent as Spinner).Prompt, 0);
            HintIsSet = true;
            (parent as Spinner).SetSelection(base.Count - 1);
        }
        if (HintIsSet && position >= base.Count - 1)
        {
            View hintView = base.GetView(0, convertView, parent);
            if (hintView is TextView)
                (hintView as TextView).SetTextAppearance(
                                                     Context, HintResource);
            return hintView;
        }
        if (HintIsSet && position < base.Count - 1)
            return base.GetView(position + 1, convertView, parent);
        return base.GetView(position, convertView, parent);
    }

    public override long GetItemId(int position)
    {
        if (HintIsSet)
        {
            if (position >= base.Count - 1)
                return -1;
            return position;
        }
        return base.GetItemId(position);
    }

    public override int Count
    {
        get
        {
            return base.Count > 0 && HintIsSet ? base.Count - 1 : base.Count;
        }
    }
}

bất kỳ ai có thể xem xét câu hỏi của tôi ?
Moeez

1

Tôi đã tìm thấy giải pháp này giải quyết được vấn đề của tôi.

final Spinner mySpinner = (Spinner)findViewById(R.id.spinner_triptype);

   final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.spinner_item, R.id.weekofday, triptype_initial);

   final ArrayAdapter<String> adapter_temp = new ArrayAdapter<String>
(this,R.layout.spinner_item, R.id.weekofday, triptype_array);


   mySpinner.setAdapter(adapter);
    mySpinner.setOnTouchListener(new View.OnTouchListener() {
       @Override
       public boolean onTouch(View v, MotionEvent event) {
       // display your error popup here
        if(flag_spinner_isFirst){
           mySpinner.setAdapter(adapter_temp);
           flag_spinner_isFirst = false;
          }
           v.onTouchEvent(event);
           return true;

       }
    });

0

Tôi nghĩ sẽ tốt hơn nếu đặt xác thực vào Danh sách mảng hơn là trên Spinner vì một khi mục được lọc, sẽ an toàn để thêm vào Spinner


0

Một cách tiếp cận khác hiệu quả nhất đối với tôi là trả về một đối tượng dạng xem trống mới. Đây là một cách tiếp cận đáng kể vì bạn không chơi với các phần tử mảng.

Tạo lớp bộ điều hợp của bạn mở rộng ArrayAdapter

bên trong phương pháp của bạn

public View getView(int position, View convertView, ViewGroup parent) {
    View row = getCustomView();
    if(position==0) // put the desired check here.
         {
            row  = new View(context);
         }
    }
    return row;
}

0

Đây là một câu hỏi rất cũ nhưng tôi đã tìm ra một cách hay (và có thể) để không hiển thị các mục đầu tiên. Lấy cảm hứng từ câu trả lời của @ Romich, tôi đã thêm logic tương tự để bỏ qua (các) mục đầu tiên.

Điều này có hiệu quả ẩn số lượng mục tùy ý (1 theo mặc định). Mã chỉ báo cáo kích thước của các đối tượng để hiển thị ngắn hơn thực tế và cũng thay đổi chỉ mục của các mục sẽ được hiển thị, vì vậy chúng tôi bỏ qua số lượng mục tùy ý.

Để giữ mọi thứ đơn giản, tôi đã loại trừ giải pháp mà tôi hiện đang sử dụng hỗ trợ ẩn danh sách các mục ngẫu nhiên, nhưng điều đó có thể dễ dàng được quản lý bằng một vài chỉnh sửa đối với mã.

class ArrayAdapterCustom(context: Context, textViewResourceId: Int, vararg objects: String)
    : ArrayAdapter<String>(context, textViewResourceId, objects) {

    //Can skip first n items (skip 1 as default)
    var hideFirstItemsCount = 1

    override fun getCount(): Int {
        return super.getCount() - hideFirstItemsCount
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
        return super.getDropDownView(position + hideFirstItemsCount, convertView, parent)
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.