Picker Dialog in Android DialogFragment

Jyotishgher Astrology
By -
0

 Creating a Custom Number Picker Dialog in Android Using Volley and DialogFragment

A step-by-step guide to building an interactive search dialog for Android applications using DialogFragment, Volley HTTP networking, and custom interfaces to fetch and select PL numbers seamlessly.In inventory, manufacturing, and enterprise applications—such as those used in rail coach production—Parts List (PL) numbers are essential for identifying items and components. Allowing users to search and select these PL numbers via an interactive dialog prevents manual typing errors and provides a cleaner user experience.

Picker Dialog in Android DialogFragment

This article covers how to create a searchable modal dialog in Android using Java, Volley for asynchronous HTTP requests, and clean UI patterns to make an EditText act as a click-to-select trigger.

Key Requirements

  • DialogFragment: To display the search interface as a floating modal window.

  • Volley Library: For lightweight, asynchronous POST requests with automatic request cancellation.

  • Debounce Handler: To introduce a short delay (300ms) while typing, preventing unnecessary network calls on every keystroke.

  • Custom Listener Interface: To pass selected PL data back to the calling Activity or Fragment.

Step 1: Designing the Layout

The search dialog layout (dialog_pl_search.xml) requires an AutoCompleteTextView for search input, a ListView for search results, a ProgressBar for loading feedback, and a fallback TextView when no records match.

XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <AutoCompleteTextView
        android:id="@+id/search_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type PL number or keyword..."
        android:inputType="text" />

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="GONE" />

    <TextView
        android:id="@+id/no_results_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="8dp"
        android:visibility="GONE" />

    <ListView
        android:id="@+id/results_list"
        android:layout_width="match_parent"
        android:layout_height="250dp" />

    <Button
        android:id="@+id/cancel_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:text="Cancel" />

</LinearLayout>

Step 2: Implementing PlSearchDialog Class

The PlSearchDialog extends DialogFragment. It parses JSON responses from the target API (PLNumberQuery.php) and uses a custom callback interface to report selection events.


public class PlSearchDialog extends DialogFragment {

    private static final String DATA_LOC = "https://jyotishgher.in/cmms/PL/PLNumberQuery.php";

    private AutoCompleteTextView searchInput;

    private ListView resultsList;

    private ProgressBar progressBar;

    private TextView noResultsText;

    private Button cancelButton;


    private ArrayAdapter<String> resultsAdapter;

    private ArrayList<String> displayList = new ArrayList<>();

    private ArrayList<JSONObject> plDataList = new ArrayList<>();


    private RequestQueue requestQueue;

    private Handler handler = new Handler();

    private Runnable searchRunnable;


    private OnPlSelectedListener listener;


    public interface OnPlSelectedListener {

        void onPlSelected(String plNo, String shortDesc, String fullDesc, String prodYr);

    }


    public void setOnPlSelectedListener(OnPlSelectedListener listener) {

        this.listener = listener;

    }


    @NonNull

    @Override

    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());

        LayoutInflater inflater = requireActivity().getLayoutInflater();

        View view = inflater.inflate(R.layout.dialog_pl_search, null);


        initializeViews(view);

        setupSearch();


        builder.setView(view).setTitle("Select PL Number");

        return builder.create();

    }


    private void initializeViews(View view) {

        searchInput = view.findViewById(R.id.search_input);

        resultsList = view.findViewById(R.id.results_list);

        progressBar = view.findViewById(R.id.progress_bar);

        noResultsText = view.findViewById(R.id.no_results_text);

        cancelButton = view.findViewById(R.id.cancel_button);


        requestQueue = Volley.newRequestQueue(requireContext());

        cancelButton.setOnClickListener(v -> dismiss());


        resultsAdapter = new ArrayAdapter<>(requireContext(),

                android.R.layout.simple_list_item_1, displayList);

        resultsList.setAdapter(resultsAdapter);


        resultsList.setOnItemClickListener((parent, view1, position, id) -> {

            try {

                JSONObject selected = plDataList.get(position);

                String plNo = selected.optString("PL_NO", "");

                String shortDesc = selected.optString("SHORT_DESC", "");

                String fullDesc = selected.optString("FULL_DESC", "");

                String prodYr = selected.optString("PROD_YR", "");


                if (listener != null) {

                    listener.onPlSelected(plNo, shortDesc, fullDesc, prodYr);

                }

                dismiss();

            } catch (Exception e) {

                e.printStackTrace();

                Toast.makeText(getContext(), "Error selecting item", Toast.LENGTH_SHORT).show();

            }

        });

    }


    private void setupSearch() {

        searchInput.setThreshold(2);

        searchInput.addTextChangedListener(new TextWatcher() {

            @Override

            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}


            @Override

            public void onTextChanged(CharSequence s, int start, int before, int count) {

                if (searchRunnable != null) {

                    handler.removeCallbacks(searchRunnable);

                }


                String query = s.toString().trim();

                if (query.length() >= 2) {

                    progressBar.setVisibility(View.VISIBLE);

                    noResultsText.setVisibility(View.GONE);

                    displayList.clear();

                    plDataList.clear();

                    resultsAdapter.notifyDataSetChanged();


                    searchRunnable = () -> searchPlNumbers(query);

                    handler.postDelayed(searchRunnable, 300);

                } else {

                    displayList.clear();

                    plDataList.clear();

                    resultsAdapter.notifyDataSetChanged();

                    progressBar.setVisibility(View.GONE);

                    noResultsText.setVisibility(View.GONE);

                }

            }


            @Override

            public void afterTextChanged(Editable s) {}

        });

    }


    private void searchPlNumbers(String query) {

        if (requestQueue != null) {

            requestQueue.cancelAll("pl_search");

        }


        StringRequest request = new StringRequest(Request.Method.POST, DATA_LOC,

                response -> {

                    progressBar.setVisibility(View.GONE);

                    try {

                        JSONObject json = new JSONObject(response);

                        JSONArray results = json.getJSONArray("result");


                        displayList.clear();

                        plDataList.clear();


                        if (results.length() == 0) {

                            noResultsText.setVisibility(View.VISIBLE);

                            noResultsText.setText("No PL items found.");

                        } else {

                            noResultsText.setVisibility(View.GONE);

                            for (int i = 0; i < results.length(); i++) {

                                JSONObject item = results.getJSONObject(i);

                                plDataList.add(item);

                                String plNo = item.optString("PL_NO", "");

                                String shortDesc = item.optString("SHORT_DESC", "");

                                displayList.add(plNo + " - " + shortDesc);

                            }

                        }

                        resultsAdapter.notifyDataSetChanged();

                    } catch (Exception e) {

                        e.printStackTrace();

                        noResultsText.setVisibility(View.VISIBLE);

                        noResultsText.setText("Error loading PL items.");

                    }

                },

                error -> {

                    progressBar.setVisibility(View.GONE);

                    noResultsText.setVisibility(View.VISIBLE);

                    noResultsText.setText("Network error. Check connection.");

                }) {

            @Override

            protected Map<String, String> getParams() {

                Map<String, String> params = new HashMap<>();

                params.put("pl_no", query);

                return params;

            }


            @Override

            public String getTag() {

                return "pl_search";

            }

        };


        requestQueue.add(request);

    }


    @Override

    public void onDestroy() {

        super.onDestroy();

        if (handler != null) {

            handler.removeCallbacksAndMessages(null);

        }

        if (requestQueue != null) {

            requestQueue.cancelAll("pl_search");

        }

    }

}

Step 3: Triggering Dialog from Non-Editable EditText

To configure an EditText field to open the dialog upon click—while preventing direct keyboard input—disable focusability in both layout XML and Java code.

<EditText

    android:id="@+id/etPLNumber"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:hint="Select PL Number"

    android:focusable="false"

    android:clickable="true"

    android:cursorVisible="false" />


Activity / Fragment Setup

EditText etPLNumber = findViewById(R.id.etPLNumber);
etPLNumber.setFocusable(false);
etPLNumber.setClickable(true);

etPLNumber.setOnClickListener(v -> showPLSearchDialog());

private void showPLSearchDialog() {
    PlSearchDialog dialog = new PlSearchDialog();
    dialog.setOnPlSelectedListener((plNo, shortDesc, fullDesc, prodYr) -> {
        // Set selected PL number into field
        etPLNumber.setText(plNo);
    });
    dialog.show(getSupportFragmentManager(), "PlSearchDialog");
}

  •  Using Handler.postDelayed() with a 300ms threshold prevents server spamming while users type long search queries. Calling requestQueue.cancelAll("pl_search") in onDestroy() and prior to new searches prevents race conditions and memory leaks.Combining focusable="false" with setOnClickListener repurposes a standard text field into an interactive picker without triggering soft keyboard displays.

  • Tags:

    Post a Comment

    0Comments

    Post a Comment (0)