<%= label f, :email %> <%= text_input f, :email %> <%= error_tag f, :email %>

<%= label f, :authority %>

<%= select(f,

:authority_id, ,

style: "width: 100%")

%>

<%= error_tag f, :authority_id %>

<%= submit gettext("Save") %>

<% end %>

This is a custom Phoenix form but it has the following addition which is more or less the meat of this article (along with the get_select_value function I explained before): select(f, :authority_id, , style: "width: 100%") So this will create an html select element which will contain a single value (the array in the third parameter of select): The authority of that object or the authority that the user had submitted in the form. For this it uses get_select_value to retrieve the :authority_id and if it’s not nil it passes it to get_authority! to retrieve the actual authority and return a tuple with its name and id. By default when you create a select element you’ll pass an array of all options in the third parameter, for example: select(f, :authority_id, Phxcrd.Auth.list_authorities |> Enum.map(&{&1.name, &1.id})) Of course this beats the purpose of using ajax since all options will

be rendered.

The final step is to add the required custom javascript to convert that select to select2-with-ajax:

$(function () {

$('#user_authority_id').select2({

allowClear: true,

placeholder: 'Select authority',

ajax: {

url: '<%= Routes.api_path(@conn, :search_authorities) %>',

dataType: 'json',

delay: 150,

minimumInputLength: 2

}

});

})

The JS very rather simple; the allowClear option will display an x so that you can clear the selected authority while the ajax url will be that of the :search_authorities.

CONCLUSION

Although this article may seem a little long, as I’ve already mentioned the most important thing to keep is how to properly set the value that should be displayed in your select2 widget. Beyond that everything is a walk in the park by following the docs. HOW TO CREATE A CUSTOM FILTERED ADAPTER IN ANDROID Παρ 05 Απρίλιος 2019

INTRODUCTION

Android offers a nice component named AutoCompleteTextView that can be used to auto-fill a text box from a list of values. In its simplest form, you just create an array adapter passing it a list of objects (that have a proper toString() method). Then you type some characters to the textbox and by default it will filter the results searching in the _beginning of the backing object’s toString()

result_.

However there are times that you don’t want to look at the beginning of the string (because you want to look at the middle of the string) or you don’t want to just to search in toString() method of the object or you want to do some more fancy things in object output. For this you must override the ArrayAdapter and add a custom Filter. Unfurtunately this isn’t as straightforward as I’d like and I couldn’t find a quick and easy tutorial on how it can be done. So here goes nothing: In the following I’ll show you a very simple android application that will have _the minimum viable_ custom filtered adapter implementation. You can find the whole project in github: https://github.com/spapas/CustomFilteredAdapeter but I am going to discuss everything here also.

THE APPLICATION

Just create a new project with an empty activity from Android Studio. Use kotlin as the language.

THE LAYOUT

I’ll keep it as simple as possible:

xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">

android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="32sp" android:textAlignment="center"/>

You should just care about the AutoCompleteTextView with an id of autoCompleteTextView. THE BACKING DATA OBJECT I’ll use a simple PoiDao Kotlin data class for this: data class PoiDao(

val id: Int,

val name: String,

val city: String,

val category_name: String

)

I’d like to be able to search to both name, city and category_name of each object. To create a list of the pois to be used to the adapter I can do something like: val poisArray = listOf( PoiDao(1, "Taco Bell", "Athens", "Restaurant"), PoiDao(2, "McDonalds", "Athens","Restaurant"), PoiDao(3, "KFC", "Piraeus", "Restaurant"), PoiDao(4, "Shell", "Lamia","Gas Station"), PoiDao(5, "BP", "Thessaloniki", "Gas Station")

)

THE CUSTOM ADAPTER

This will be an ArrayAdapter implementing also the Filterable

interface:

inner class PoiAdapter(context: Context, @LayoutRes private val layoutResource: Int, private val allPois: List): ArrayAdapter(context, layoutResource, allPois),

Filterable {

private var mPois: List = allPois override fun getCount(): Int {

return mPois.size

}

override fun getItem(p0: Int): PoiDao? { return mPois.get(p0)

}

override fun getItemId(p0: Int): Long { // Or just return p0 return mPois.get(p0).id.toLong()

}

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { val view: TextView = convertView as TextView? ?: LayoutInflater.from(context).inflate(layoutResource, parent, false) as TextView view.text = "${mPois.name} ${mPois.city} (${mPois.category_name})"

return view

}

override fun getFilter(): Filter { // See next section

}

}

You’ll see that we add an instance variable named mPois that gets initialized in the start with allPois (which is the initial list of all pois that is passed to the adapter). The mPois will contain the _filtered_ results. Then, for getCount and getItem we return the corresponding valeus from mPois; the getItemId is used when you have an sqlite backed adapter but I’m including it here

for completeness.

The getView will create the specific line for each item in the dropdown. As you’ll see the layout that is passed must have a text child which is set based on some of the attributes of the corresponding poi for each position. Notice that we can use whatever view layout we want for our dropdown result line (this is the layoutResource parameter) but we need to configure it (i.e bind it with the values of the backing object) here properly. Finally we create a custom instance of the Filter, explained in the

next section.

THE CUSTOM FILTER

The getFilter creates an object instance of a Filter and returns it: override fun getFilter(): Filter { return object : Filter() { override fun publishResults(charSequence: CharSequence?, filterResults: Filter.FilterResults) { mPois = filterResults.values as List notifyDataSetChanged()

}

override fun performFiltering(charSequence: CharSequence?): Filter.FilterResults { val queryString = charSequence?.toString()?.toLowerCase() val filterResults = Filter.FilterResults() filterResults.values = if (queryString==null || queryString.isEmpty())

allPois

else

allPois.filter {

it.name.toLowerCase().contains(queryString) || it.city.toLowerCase().contains(queryString) || it.category_name.toLowerCase().contains(queryString)

}

return filterResults

}

}

}

This object instance overrides two methods of Filter: performFiltering and publishResults. The performFiltering is where the actual filtering is done; it should return a FilterResults object containing a values attribute with the filtered values. In this method we retrieve the charSequence parameter and converit it to lowercase. Then, if this parameter is not empty we filter the corresponding elements of allPois (i.e name, city and category_name in our case) using contains. If the query parameter is empty then we just return all pois. Warning java developers; here the if is used as an expression (i.e its result will be assigned to filterResults.values). After the performFiltering has finished, the publishResults method is called. This method retrieves the filtered results in its filterResults parameter. Thus it sets mPois of the custom adapter is set to the result of the filter operation and calls notifyDataSetChanged to display the results. USING THE CUSTOM ADAPTER To use the custom adapter you can do something like this in your activity’s onCreate: override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val poisArray = listOf( // See previous sections

)

val adapter = PoiAdapter(this, android.R.layout.simple_list_item_1, poisArray) autoCompleteTextView.setAdapter(adapter) autoCompleteTextView.threshold = 3 autoCompleteTextView.setOnItemClickListener() { parent, _, position, id -> val selectedPoi = parent.adapter.getItem(position) as PoiDao? autoCompleteTextView.setText(selectedPoi?.name)

}

}

We create the PoiAdapter passing it the poisArray and android.R.layout.simple_list_item_1 as the layout. That layout just contains a textview named text. As we’ve already discussed you can pass something more complex here. The thresold defined the number of characters that the user that needs to enter to do the filtering

(default is 2).

Please notice that when the user clicks (selects) on an item of the dropdown we set the contents of the textview (or else it will just use the object’s toString() method to set it).

← Older

Atom | RSS

SUBSCRIBE

Do you want to get notified via email when I post new content? Click

here to subscribe!

RECENT POSTS

* Declarative Ecto query sorting * How to properly handle an HTML form * Declarative Ecto query filters * Phoenix forms integration with select2 and ajax * How to create a custom filtered adapter in Android

CATEGORIES

* android

* css

* django

* elixir

* flask

* git

* html

* javascript

* pelican

* postgresql

* python

* spring

* unix

* wagtail

TAGS

less , rest

, query

, ecto

, tables

, pivot_table

, console

, async

, django-rq

, github-pages

, forms

, yaml

, 404

, ldap

, immutable

, init.d

, youtube-dl

, python

, django-extensions

,

class-based-views

, scipy

, grid

, ag-grid

, du

, watchify

, settings

, flux

, FixedDataTable

, queries

, debug

, imgur

, security

, npm

, hyperapp

, spring

, react-redux

, cron

, babelify

, design

, tutorial

, gmail

, reportlab

, rq

, redis

, python-3

, research

, werkzeug

, jobs

, redux-thunk

, disk-usage

, javascript

, component

, ajax

, elixir

, ipython

, uglify

, configuration

, properties

, bash

, es6

, spring-boot

, postgresql

, kotlin

, adapter

, filter

, flask

, notebook

, static-html

, pdf

, react-notification

, pelican

, tasks

, node.js

, unix

, google

, linux

, pivot

, auditing

, git

, java

, introduction

, pusher

, fixed-data-tables

, heroku

, xhtml2pdf

, numpy

, form

, django-rest-auth

, wagtail

, rst

, python-2

, ffmpeg

, middleware

, profiles

, spring-security

, github.io

, error

, asynchronous

, react-router-redux

, select2

, history

, django-rest-framework

, redux-form

, cbv

, scheduling

, plpgsql

, boilerplate

, generic

, babel

, authentication

, html

, android

, pandas

, css

, node

, phoenix

, deploy

, mongodb

, react

, autocompelte

, declarative

, php

, browserify

, branching

, github

, jupyter

, windows

, youtube

, django

, q

, react-router

, redux

, boostrap-material-design

YEARLY ARCHIVES

2013 , 2014

, 2015

, 2016

, 2017

, 2018

, 2019

GITHUB REPOS

* django-authorities An application for managing your organization's authorities (departments, directorates etc) * cookiecutter-django-starter Start a django project using a cookiecutter

* django-modernize

Easy modernization of Django projects * django-generic-scaffold Quick generation of CRUD generic views for django! * CustomFilteredAdapeter A Custom Filtered Adapeter for Android

@spapas on GitHub

Copyright © 2013–2019 Serafeim Papastefanos — Powered by Pelican

Details

1