How to create an empty view in a RecyclerView? - android

I am working with a Fragment that handles a RecyclerView with different adapters, but when I try to show my emptyView TextView in the RecyclerView it simply stops the RecyclerView from working properly.
I created the following filter in my adapter:
/**
* Method that filters the data using the onQueryTextSubmit and onQueryTextChange.
*
* #return a Filter class that calls the method performFiltering of the FilterResults class
* and this method applies the filter and returns a list with the resulting data filtered.
*/
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence querySample) {
if (emptyView.getVisibility() == View.VISIBLE) {
emptyView.setVisibility(View.GONE);
}
/*
* Verifies the value of the sampleNameSearched and compares the data.
*/
if (querySample == null) {
// Updates the recyclerView with the sampleList.
sampleListFiltered.submitList(sampleList);
// holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
} else {
// Cleans the accentuation, letter case and other symbols of the querySample.
sampleNameSearched = Normalizer.normalize(querySample.toString(), Normalizer.Form.NFD)
.replaceAll("[^\\p{ASCII}]", "").toLowerCase();
// Creates the list that will save the filtered samples.
List<Sample> filteredSampleList = new LinkedList<>();
// Gets the data filtered in the for loop.
for (Sample sample : sampleList) {
// Cleans the accentuation, letter case and other symbols.
String sampleName = Normalizer.normalize(sample.getName(), Normalizer.Form.NFD)
.replaceAll("[^\\p{ASCII}]", "").toLowerCase();
// Adds the sample that matches the filter in the filteredSampleList.
if (sampleName.contains(sampleNameSearched)) {
filteredSampleList.add(sample);
}
}
// Updates the RecyclerView.
sampleListFiltered.submitList(filteredSampleList);
// TODO: Find a solution for not showing the emptyView instantly (not entering the onBindViewHolder).
/*
* if filteredSampleList is empty, shows the emptyView with the proper message.
*/
if (filteredSampleList.isEmpty()) {
// Updates the RecyclerView.
emptyView.setVisibility(View.VISIBLE);
} else {
if (emptyView.getVisibility() == View.VISIBLE) {
emptyView.setVisibility(View.GONE);
}
}
}
// Returns the filterResults.
FilterResults filterResults = new FilterResults();
filterResults.values = sampleListFiltered;
return filterResults;
}
// Publish the results on the RecyclerView.
#Override
protected void publishResults(CharSequence constraint, FilterResults filterResults) {
notifyDataSetChanged();
}
};
}
I cannot find out why my emptyView.setVisibility() sometimes works and others simply doesn't.
My layout is the following:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#id/search_sample_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintTop_toBottomOf="#+id/sample_folder_selector">
<SearchView
android:id="#+id/sample_search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:background="#drawable/search_box_border"
android:clickable="true"
android:iconifiedByDefault="false"
android:layoutDirection="rtl"
android:queryHint="#string/sample_search" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/ListNavigatorRecyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="25dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toTopOf="#+id/ButtonPanel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/search_sample_field"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/emptyListNavigatorView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:layout_marginBottom="100dp"
android:gravity="center"
android:text=""
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/ButtonPanel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/search_sample_field"
app:layout_constraintVertical_bias="1.0" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/ButtonPanel"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#color/BaseColor_4"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent">
<ImageButton
android:id="#+id/remove_sample_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:src="#drawable/btn_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
style="#style/IconButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Just to make sure that you understand the problem you can see some prints of the problem below:
What was supposed to happen:
Step 1 - Filter:
Filtering
Step 2 - Writing a sample that doesn't exist:
Sample that doesn't exist
Step 3 - Removing the wrong character:
The wrong character removed
What is happening right now (not supposed to happen):
Step 1 - Filter (working):
Filtering
Step 2 - Writing a sample that doesn't exist (not showing the emptyView):
Not showing the emptyView
Step 3 - Removing the wrong character (not showing the sample nor the bold character):
Not showing the sample
If I press the search button on the keyboard it works, but it was intended to work without pressing the search button...
Can someone help me with this problem?
I used a toast, and the toast works, but when it comes to using any kind of set method in the performFiltering or layout that requires a set method it seems that Android somehow losses its working flow.
I tried the following implementations:
How to show an empty view with a RecyclerView?
https://alexzh.com/how-to-setemptyview-to-recyclerview/
https://www.reddit.com/r/androiddev/comments/3bjnxi/best_way_to_handle_recyclerview_empty_state/

How to solve this problem?
I created an EmptyRecyclerView that extends the RecyclerView behaviours and added an updateEmptyView() method that verifies how many items I have in my RecyclerView and updates the view accordingly. My solution was based on this answer by maff91. And another source that helped me solve the problem is this Google Application that creates an EmptyView message like the one I needed.
My thanks to maff91 and the Google Application that has the same behaviour.

Related

Picasso showing images after scrolling

I have a searchview and recyclerview , I share the xml code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SearchView
android:id="#+id/searchinput"
android:layout_width="match_parent"
android:background="#FFFFFF"
android:searchIcon="#null"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/searchinput"
android:layout_alignParentStart="true" />
</RelativeLayout>
Now I make request to web onQueryTextSubmit(String query), I call a function in my adapter class where I do the following
public void filterData(String query){
query=query.toLowerCase();
if(query.isEmpty()){
images.clear();
images.addAll(orignallist);
notifyDataSetChanged();
}
else {
// This is main function
getimages(query);
}
}
I here get images url in a separate thread from the web through jsoup and show it in the recycler view through piccaso, it works perfectly but it only shows when I scroll down a bit , I use this with piccaso in onbindviewholder()
Picasso.get().load(images.get(position)).fit().networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE).centerInside().into(holder.imageView);
How to show image without scrolling , thank you!
When search button clicked , when scrolled down a bit
Update : after adding more functions it is now showing only when the keypad pops up
Problem was that I was trying to use notifydatasetchanged() inside thread , so I passed activity to the adapter and implemented this :
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
Images where showing on scrolling or keypad opening because adapter items were getting recycled

Why is my AutoCompleteTextView not working?

This feels strange as normally the widgets work as they are supposed to. I have an AutoCompleteTextView that I want to populate with a list of city names. It seems simple but doesn't work as I intend to. Here is the resulting output:
Here is the layout in picture:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity1">
<AutoCompleteTextView
android:id="#+id/autocomptv_city_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:background="#drawable/bg_edittext_rect_opd"
android:hint="Select City"
android:text=""
android:inputType="text"
android:maxLines="1"
android:completionThreshold="0"
android:padding="10dp"
android:singleLine="true"
android:textColor="#000000"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
And below is the java code for the same:
public class MainActivity1 extends AppCompatActivity {
AutoCompleteTextView mAutCompleteTextViewSelfCity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
mAutCompleteTextViewSelfCity = ((AutoCompleteTextView) findViewById(R.id.autocomptv_city_list));
setupCityDropdownwidget();
}
private void setupCityDropdownwidget() {
Type listType = new TypeToken<List<CityName>>() {}.getType();
List<CityName> citiesList = Singletons.getGsonInstance().fromJson(TestData.cityDataJson, listType);
CityArrayAdapter adapter = new CityArrayAdapter(this, R.layout.item_spinner_city, citiesList);
mAutCompleteTextViewSelfCity.setAdapter(adapter);
adapter.notifyDataSetChanged();
mAutCompleteTextViewSelfCity.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
CityName selectedCitySelf = ((CityName) parent.getItemAtPosition(position));
mAutCompleteTextViewSelfCity.setText(selectedCitySelf.getCityName());
}
});
}
}
Problem:
Well, I want the view to be such that as soon as the user taps on it, it shows a dropdown of cities and when the user starts typing their city for filtering, the view keeps showing narrowed down suggestions for the same.
Currently, the only time it is able to suggest is when I type in something and empty out the text view. If I change the completion threshold to 1, no suggestions are shown ever.
What's wrong with my code?
Here is the complete source for reference: https://wetransfer.com/downloads/ce4017f5f2488288ef7494dc029e033420191019092536/7afa9a3e64afb257293533bd634d6c3220191019092536/dc2341
So ultimately, it turned out to be about the basics - The data item that ArrayAdapter works with, should provide a meaningful toString() override. That is what had been missing from my implementation.
From the docs:
By default, the array adapter creates a view by calling
Object#toString() on each data object in the collection you provide,
and places the result in a TextView.
I did end up wasting up some time but the experience and knowledge would surely come in handy some day.
YOu don't need to set adapter.notifyDataSetChanged();, just set your adapter and everything goes fine.

RecyclerView scroll returning to the top of the list when add new items [duplicate]

This question already has answers here:
How to update RecyclerView Adapter Data
(16 answers)
Closed 3 years ago.
I'm developing a application that the RecyclerView load new data when the user hit the bottom of the scroll. However, the list is already completed and have it's fixed size, I just want to put the content in the Recycler, but when I insert the content in the Recycler, the scroll returns to the top of the list. And then I need to scroll down all over again until I reach the new data that was inserted in to the bottom of the list. All that I want is to maintain the scroll position when the new data is loaded.
I've tried use descendantFocusability = "blocksDescendants", but this didn't work.
This is my XML code where the RecyclerView can be found
<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:headerLayout="#layout/top_navigation_bar"
tools:context=".MainActivity"
android:descendantFocusability="blocksDescendants">
<RelativeLayout
android:id="#+id/productsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:orientation="vertical"
android:background="#EBE7E7"
>
<TextView
android:id="#+id/productsLayoutTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:lineSpacingExtra="0sp"
android:text="#string/produtos_em_destaque"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="normal"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/productsLayoutTitle"
/>
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:layout_alignBottom="#+id/recyclerView"
android:scaleX="1"
android:scaleY="1"
android:progressTint="#1C36FF"
/>
</RelativeLayout>
</RelativeLayout>
The code that is used to add data to the RecyclersView is
fun showData(products:Product,productsListSize:Int){
if(!isStarted){
recyclerView.apply{
layoutManager=LinearLayoutManager(this#MainActivity)
adapter=ViewHolderAdapter(products,productsListSize)
(adapter as ViewHolderAdapter).notifyItemRangeInserted(0,productsListSize)
pageNumber++
}
progressBar.visibility=View.GONE
isStarted=true
}else{
recyclerView.apply{
adapter=ViewHolderAdapter(products,pageNumber*limitPerPage)
(adapter as ViewHolderAdapter).notifyItemRangeInserted(((pageNumber-1)*limitPerPage),pageNumber*limitPerPage)
pageNumber++
}
progressBar.visibility=View.GONE
}
}
My ViewHolderAdapter
packagecom.example.kotlinbasics
importandroid.graphics.Color
importandroid.view.LayoutInflater
importandroid.view.View
importandroid.view.ViewGroup
importandroid.widget.ImageView
importandroid.widget.RatingBar
importandroid.widget.TextView
importandroidx.recyclerview.widget.RecyclerView
importcom.squareup.picasso.Picasso
importkotlinx.android.synthetic.main.recyclerview_layout.view.*
class ViewHolderAdapter(private val products:Product,private val productsListSize:Int):RecyclerView.Adapter<ViewHolderAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent:ViewGroup,viewType:Int):ViewHolder{
val view=LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_layout,parent,false)
return ViewHolder(view)
}
override fun getItemCount()=productsListSize
override fun onBindViewHolder(holder:ViewHolder,position:Int){
holder.productName.text=products.produtos[position].nome
Picasso.get().load(products.produtos[position].img).into(holder.productImage)
}
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
val productName:TextView=itemView.ProductName
val productImage:ImageView=itemView.ProductImage
}
}
All application works really fine, the only problem is with that scroll behavior.
I expected that the scroll position didn't goes to the top of the RecyclersView list when I add new data in him.
As you are creating a new adapter each time, the RecyclerView will be always going back to the start position. You have to update the current adapter, and not to create another adapter with the entire list.
You should have a method on your adapter to manage the item list, such as
fun updateList(product: Product) {
myList.add(product.list)
}
And in your else branch you will need to update the list
recyclerView.apply{
(adapter as? ViewHolderAdapter)?.updateList(products)
}
just use code bellow, and input requared position (in your case - last position)
recyclerView.scrollToPosition()
Create one instance of your recycler view adapter , the pass the orignal list to it . whenever you want to add the any data , add it to the list , and then notify the adapter.
In your case , Just add the product list and notify it
private list<product> list = newArrayList<>();
adapter= new ViewHolderAdapter(list);
OnClick of button when you want to add data , just insert it into the list
list.addAll(productlist);
And now notify it to the adapter
adapter.notifyDataSetChange();
In your case
fun showData(products:Product,productsListSize:Int){
if(!isStarted){
list.addAll(product)
adapter.notifyDataSetChange();
pageNumber++
}
progressBar.visibility=View.GONE
isStarted=true
}
progressBar.visibility=View.GONE
}
}

Android: single TextView is null

I just updated my test smartphone from Android 4.1.1 to 4.1.2 and I am getting a NullPointerException that didn't show up before.
This is the onCreate() and follow up methods. Note that the function isOnline() is working well.
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
//fillStatusBar(isOnline());
fillStatusBar(false);
}
private void fillStatusBar(boolean isOnline){
TextView txt = (TextView) findViewById(R.id.txt_menu_connection_status);
if(isOnline){
//if(txt != null){
txt.setBackgroundColor(0xFF78AB46); // NullPointerException!!!
txt.setText("online");
//}
}else{
// TODO also an alert
txt.setBackgroundColor(0xFFF08080);
txt.setText("offline");
}
}
In activity_menu.xml (example, the first button here is shown normally):
<!-- (...) -->
<TextView style="#style/MainButton"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:layout_marginBottom="20dip"
android:text="#string/info"
android:onClick="onClickInfo"
/>
<TextView
android:id="#+id/txt_menu_connection_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="2dip"
/>
I commented the line in which the NullPointerException gets thrown. If I make the check if(txt != null), no exception is thrown and the layout is showed normally WITHOUT the single TextView. How is this even possible? What can I provide additionally to evaluate this case?
Following an image of the LogCat. The log disappears after a few seconds, also when saving the log it is empty.

Get Search results from Google in android app

In my android application I want to get search query from user, and search google with that query, get search results and populate a list with the search results. Custom Search API limits to 100 free searches per day. So is there any alternative for searching?
This is something which you can use.
http://google.com/complete/search?output=toolbar&q=query
It returns an XML file. Parse that xml to get the results.
But google may change the format of the query in future. Thats the only concern here. Otherwise it works great.
For future reference, note the following queries for other useful websites. Some return in JSON and others in XML formats.
http://suggestqueries.google.com/complete/search?hl=en&ds=yt&client=youtube&hjson=t&cp=1&q=query&alt=json
http://search.yahooapis.com/WebSearchService/V1/relatedSuggestion?appid=YahooDemo&query=query
http://en.wikipedia.org/w/api.php?action=opensearch&search=query&limit=10&namespace=0&format=json
http://anywhere.ebay.com/services/suggest/?q=query&s=0
http://completion.amazon.com/search/complete?method=completion&q=query&search-alias=aps&mkt=1
http://api.bing.net/osjson.aspx?Query=query&Market=en-us
You can try using this code
MainActivity.java
private EditText editTextInput;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_g__search);
editTextInput = (EditText) findViewById(R.id.editTextInput);
}
public void onSearchClick(View v)
{
try {
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
String term = editTextInput.getText().toString();
intent.putExtra(SearchManager.SUGGEST_URI_PATH_QUERY, term);
startActivity(intent);
} catch (Exception e) {
// TODO: handle exception
}
}
Activity_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<EditText
android:id="#+id/editTextInput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10" >
<requestFocus />
</EditText>
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="#+id/editTextInput"
android:layout_below="#+id/editTextInput"
android:layout_marginRight="43dp"
android:layout_marginTop="60dp"
android:onClick="onSearchClick"
android:text="CLICK" />
Also add permission for internet

Categories

Resources