Converting JSON Array into Java Objects and attaching to ListView - java

I'm relatively new to Android development and I've been stuck on this for a couple of weeks.
I'm working with a JSON Array and I've been able to log that I am returning the JSON Array with an API call and that it is breaking up the array into separate JSON Objects.
Hero JSON {"PrimaryName":"Abathur","ImageURL":"Abathur","AttributeName":"Abat","Group":"Specialist","SubGroup":"Utility","Translations":"Abatur,АБАТУР,아바투르,阿巴瑟"}
Hero JSON {"PrimaryName":"Alarak","ImageURL":"Alarak","AttributeName":"Alar","Group":"Assassin","SubGroup":"Ambusher","Translations":"亞拉瑞克,阿拉纳克,알라라크,Аларак"}
etc...
From what it appears it is converting those JSON Objects into Java Objects.
hotsbuddy.HeroDataModel#f27ef8a
hotsbuddy.HeroDataModel#db939fb
etc...
When I changed the Log to return the Hero Name, Hero Image and Hero Group from my
public static HeroDataModel fromJson(JSONObject jsonObject)
method I get this:
03-24 18:44:53.828 30539-30539/com.timfreebernii.hotsbuddy D/HoTS: Hero Abathur Abathur Specialist
03-24 18:44:53.828 30539-30539/com.timfreebernii.hotsbuddy D/HoTS: Hero Alarak Alarak Assassin
03-24 18:44:53.828 30539-30539/com.timfreebernii.hotsbuddy D/HoTS: Hero Alexstrasza Alexstrasza Support
So I can see that I am getting Java Objects back.
These logs are being placed in my HeroDataModel class. However, I'm having an issue attaching these Java Objects to my ListView and ListView Adapter.
When I log my ArrayList creation in my API call I am getting Java Objects returned.
Array [com.timfreebernii.hotsbuddy.HeroDataModel#f27ef8a,
com.timfreebernii.hotsbuddy.HeroDataModel#db939fb, etc...]
These objects are not showing up in App View. I'm not getting a list just a blank white background with the blue bar with my app name from the Main Layout.
I've been using these guides from CodePath but I'm just not quite able to finish off this feature.
https://guides.codepath.com/android/Using-an-ArrayAdapter-with-ListView#row-view-recycling
https://guides.codepath.com/android/Converting-JSON-to-Models#bonus-setting-up-your-adapter
I know I'm using a different type of Adapter but I was kind of following along from a couple simple apps I built from a Udemy course I completed.
Here is the API I'm working with:
https://api.hotslogs.com/Public/Data/Heroes
My GitHub repo:
https://github.com/tfreebern2/hotsbuddy
Here is my HeroDataModel code:
public class HeroDataModel {
private String mHeroName;
private String mHeroImage;
private String mHeroGroup;
public String getHeroName() {
return this.mHeroName;
}
public String getHeroImage() {
return this.mHeroImage;
}
public String getHeroGroup() {
return this.mHeroGroup;
}
public static HeroDataModel fromJson(JSONObject jsonObject) {
HeroDataModel h = new HeroDataModel();
try {
h.mHeroName = jsonObject.getString("PrimaryName");
h.mHeroImage = jsonObject.getString("ImageURL");
h.mHeroGroup = jsonObject.getString("Group");
} catch (JSONException e) {
e.printStackTrace();
return null;
}
return h;
}
public static ArrayList<HeroDataModel> fromJson(JSONArray jsonObjects) {
JSONObject heroJson;
ArrayList<HeroDataModel> heroes = new ArrayList<HeroDataModel> . ();
for (int i = 0; i < jsonObjects.length(); i++) {
try {
heroJson = jsonObjects.getJSONObject(i);
// Log.d("HotS", "Hero JSON " + heroJson);
} catch (JSONException e) {
e.printStackTrace();
continue;
}
HeroDataModel hero = HeroDataModel.fromJson(heroJson);
// Log.d("HoTS", "Hero " + hero);
if (hero != null) {
heroes.add(hero);
}
}
// Log.d("HoTS", "Heroes Array" + heroes);
return heroes;
}
}
My HeroListAdapter:
public class HeroListAdapter extends ArrayAdapter<HeroDataModel> {
public HeroListAdapter(HeroListActivity context, ArrayList<HeroDataModel> heroes) {
super(context, 0, heroes);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
HeroDataModel currentHero = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(
R.layout.hero_list_item, parent, false);
}
TextView heroNameView = convertView.findViewById(R.id.hero_name);
TextView heroImageView = convertView.findViewById(R.id.hero_image);
TextView heroGroupView = convertView.findViewById(R.id.hero_group);
heroNameView.setText(currentHero.getHeroName());
heroImageView.setText(currentHero.getHeroImage());
heroGroupView.setText(currentHero.getHeroGroup());
return convertView;
}
}
My HeroListActivity:
public class HeroListActivity extends AppCompatActivity {
final String HEROES_URL = "https://api.hotslogs.com/Public/Data/Heroes";
ArrayList<HeroDataModel> heroes;
HeroListAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hero_list);
heroListAPI(HEROES_URL);
ListView listView = (ListView) findViewById(R.id.lvHeroes);
heroes = new ArrayList<HeroDataModel>();
adapter = new HeroListAdapter(this, heroes);
listView.setAdapter(adapter);
}
private void heroListAPI(String url) {
AsyncHttpClient client = new AsyncHttpClient();
client.get(url, new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
ArrayList<HeroDataModel> heroes = HeroDataModel.fromJson(response);
heroes.clear(); // clear existing items if needed
heroes.addAll(HeroDataModel.fromJson(response)); // add new items
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(int statusCode, Header[] headers, Throwable e, JSONArray response) {
Log.e("HoTS", "Fail " + e.toString());
Log.d("HoTS", "Status code " + statusCode);
Toast.makeText(HeroListActivity.this, "Request Failed", Toast.LENGTH_SHORT).show();
}
});
}
}
My Hero_List Layout File:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rlLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${packageName}.${activityClass}" >
<ListView
android:id="#+id/lvHeroes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" >
</ListView>
</RelativeLayout>
My Hero_List_Item Layout File:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.timfreebernii.hotsbuddy.HeroActivity">
<TextView
android:id="#+id/hero_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hero Name"
/>
<TextView
android:id="#+id/hero_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hero Image"
/>
<TextView
android:id="#+id/hero_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hero Group"
/>
</LinearLayout>

Use Gson Library or retrofit to convert json objects into model class

Well I found a solution with some help.
My HeroListActivity is where I was making the API call and trying to convert the Json into Java Objects.
Here I declared my adapter in the HeroListActivity class outside of my onCreate method and removed the declaration of the ArrayList. I created a new HeroListAdapter using 'this' and 'new' ArrayList() as parameters. Then I attached the adapter to my ListView.
public class HeroListActivity extends AppCompatActivity {
final String HEROES_URL = "https://api.hotslogs.com/Public/Data/Heroes";
HeroListAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hero_list);
heroListAPI(HEROES_URL);
ListView listView = (ListView) findViewById(R.id.lvHeroes);
adapter = new HeroListAdapter(this, new ArrayList<HeroDataModel>());
listView.setAdapter(adapter);
}
... more code
}
In my onSuccess method I created an ArrayList named 'myHeroes' with my renamed fromJsonToModelList.
#Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
ArrayList<HeroDataModel> myHeroes = HeroDataModel.fromJsonToModelList(response);
adapter.clear();
adapter.addAll(myHeroes);
adapter.notifyDataSetChanged();
}
I clear the adapter of any previous data, add all objects to the adapter and notify the adapter of any changes.
Some of the changes were just refactoring suggestions.
Ultimately, I wasn't passing the data to my adapter correctly nor do I think creating it correctly in my onCreate method.

Related

Issues with ActivityMainBindingImpl.java

I'm writing a celsius-farenheits converter but the program crashes for something that I didn't found
I'm actually trying to use the data binding and the view model but Android Studio founded some issues in ActivityMainBindingImpl.java that I didn't write by myself. Here's the part of code were it founds a problem. It is in line 104 at com.example.convertitorecelsius_farenheit.MainViewModel viewModel = mViewModel; It says "Cannot resolve symbol 'mViewModel'"
There's another problem in line 33 in "super(bindingComponent, root, 0", it says "'ActivityMainBinding()' has private access in 'com.example.convertitorecelsius_farenheit.databinding.ActivityMainBinding'"
The last problem is at line 8 in "public class ActivityMainBindingImpl extends ActivityMainBinding {", the error is in "ActivityMainBinding", it says "Cannot inherit from final 'com.example.convertitorecelsius_farenheit.databinding.ActivityMainBinding'"
Here's the full code where I founded these problems
package com.example.convertitorecelsius_farenheit.databinding;
import com.example.convertitorecelsius_farenheit.R;
import com.example.convertitorecelsius_farenheit.BR;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.View;
#SuppressWarnings("unchecked")
public class ActivityMainBindingImpl extends ActivityMainBinding {
#Nullable
private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
#Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = new android.util.SparseIntArray();
sViewsWithIds.put(R.id.cambiaTemperatura, 3);
sViewsWithIds.put(R.id.inputTemperatura, 4);
sViewsWithIds.put(R.id.converti, 5);
}
// views
#NonNull
private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;
// variables
// values
// listeners
// Inverse Binding Event Handlers
public ActivityMainBindingImpl(#Nullable androidx.databinding.DataBindingComponent bindingComponent, #NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 0
, (android.widget.Button) bindings[3]
, (android.widget.Button) bindings[5]
, (android.widget.EditText) bindings[4]
, (android.widget.TextView) bindings[2]
, (android.widget.TextView) bindings[1]
);
this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
this.textTemperatura.setTag(null);
this.textView.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
#Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x2L;
}
requestRebind();
}
#Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
#Override
public boolean setVariable(int variableId, #Nullable Object variable) {
boolean variableSet = true;
if (BR.viewModel == variableId) {
setViewModel((com.example.convertitorecelsius_farenheit.MainViewModel) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setViewModel(#Nullable com.example.convertitorecelsius_farenheit.MainViewModel ViewModel) {
this.mViewModel = ViewModel;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.viewModel);
super.requestRebind();
}
#Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
}
return false;
}
#Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String viewModelTypeCurrentTemperature = null;
int viewModelConvertiTemperatura = 0;
com.example.convertitorecelsius_farenheit.MainViewModel viewModel = mViewModel;
if ((dirtyFlags & 0x3L) != 0) {
if (viewModel != null) {
// read viewModel.typeCurrentTemperature
viewModelTypeCurrentTemperature = viewModel.getTypeCurrentTemperature();
// read viewModel.convertiTemperatura()
viewModelConvertiTemperatura = viewModel.convertiTemperatura();
}
}
// batch finished
if ((dirtyFlags & 0x3L) != 0) {
// api target 1
this.textTemperatura.setText(viewModelConvertiTemperatura);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.textView, viewModelTypeCurrentTemperature);
}
}
// Listener Stub Implementations
// callback impls
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): viewModel
flag 1 (0x2L): null
flag mapping end*/
//end
}
Here's the program I wrote
MainActivity.java
package com.example.convertitorecelsius_farenheit;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.EditText;
import com.example.convertitorecelsius_farenheit.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private MainViewModel viewModel;
public EditText inputTemperature;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inputTemperature = findViewById(R.id.inputTemperatura);
binding = ActivityMainBinding.inflate(getLayoutInflater());
binding.setLifecycleOwner(this);
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
binding.setViewModel(viewModel);
}
public int getInputTemperature() {
return Integer.parseInt(inputTemperature.toString());
}}
MainViewModel.java
package com.example.convertitorecelsius_farenheit;
import androidx.lifecycle.ViewModel;
public class MainViewModel extends ViewModel {
public int grades;
public boolean isCelsius = false;
MainActivity temperaturaInserita = new MainActivity();
//private final MutableLiveData<String> _TypeCurrentTemperatura = new MutableLiveData<>();
private String _TypeCurrentTemperatura = ""; //indicates if the temperature is celsius or farenheit
public String getTypeCurrentTemperature() {
return _TypeCurrentTemperatura;
}
public void changeTypeTemperature() {
if (isCelsius) {
isCelsius = false;
_TypeCurrentTemperatura = "F°";
} else {
isCelsius = true;
_TypeCurrentTemperatura = "C°";
}
}
public int convertiTemperatura() { //convertTemperature (that's the italian name)
if (isCelsius) {
grades = (int) ((temperaturaInserita.getInputTemperature() * 1.8) + 32);
} else {
grades = (int) ((int) ((temperaturaInserita.getInputTemperature()) -32) * .5556);
}
return grades;
}}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="viewModel"
type="com.example.convertitorecelsius_farenheit.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="348dp"
android:text="#{viewModel.typeCurrentTemperature}"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/cambiaTemperatura"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="80dp"
android:text="c--f"
app:layout_constraintBottom_toTopOf="#+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="#+id/inputTemperatura"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="52dp"
android:ems="10"
android:inputType="number"
app:layout_constraintBottom_toTopOf="#+id/cambiaTemperatura"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/converti"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="273dp"
android:layout_marginBottom="274dp"
android:text="converti"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/inputTemperatura"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/textTemperatura"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="23dp"
android:text="#{viewModel.convertiTemperatura()}"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="#+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Can somebody help me :)
There are some things wrong in your code:
You're setting the activity view twice, remove the first line: setContentView(R.layout.activity_main);
Since you're using view binding (different thing from data binding), you dont need to call findViewById replace it with inputTemperature = binding.inputTemperatura
If you're already using data binding why bother with view binding? You can do all input/output related tasks in data binding.
You SHOULD NEVER instantiate ANDROID activities or hold a reference to it, this is task of the Android framework, remove the line MainActivity temperaturaInserita = new MainActivity(); of your viewmodel.
Check this answer it may help you: Android : Difference between DataBinding and ViewBinding
EDIT
You don't need to call methods of your activity from your viewmodel, this is a bad practice, because if the system destroys your activity you will end with a NPE in your view model, you have 2 options:
Use two way data binding to set/get the value of the temperature from the viewmodel: https://bignerdranch.com/blog/two-way-data-binding-on-android-observing-your-view-with-xml/
Change the function in the view model to receive as argument the value of the input text.
I recommend to go with first option, this way you will have the updated value always in your view model and also can survive config changes.
And remember don't matter what, your viewmodel SHOULD never have a reference to the Activity.

Why is my Android ImageView empty (loading up through Uri, Bitmap or using Picasso)

I have a fragment to display a queue of either videos of images. The video I display in the VideoView works fine, it replays, it's golden. But the images I put in the ImageView just appear invisible. I tried loading them through Uri, by reading a Bitmap, now they're set up with Picasso, and none of it fixed it. The AssetObtainer you'll see in MultimediaPlayer works with both sound files and videos so far, so I highly doubt it has an issue with images. Here's the code:
MultimediaPlayer.java :
public class MultimediaPlayer extends Fragment
{
VideoView mVideoView;
ImageView mImageView;
MultimediaViewModel mMultimediaViewModel;
Play mPlayThread;
Activity mActivity;
AssetObtainer assetObtainer = new AssetObtainer();
public Long mTutorialId;
public List<Multimedia> multimedias = new LinkedList<>();
#Override
public View onCreateView(#NotNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_multimedia_player, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
mActivity = requireActivity();
mMultimediaViewModel = new ViewModelProvider(this).get(MultimediaViewModel.class);
mVideoView = view.findViewById(R.id.video_embed);
mImageView = view.findViewById(R.id.image_embed);
getPlayer(0);
}
private void getPlayer(int position)
{
if(mPlayThread != null) {
mPlayThread.interrupt();
position++;
}
if(!multimedias.isEmpty()) {
mPlayThread = new Play(multimedias.get(position));
mPlayThread.start();
}
}
private class Play extends Thread
{
private final Multimedia currentMedia;
Play(Multimedia media){
currentMedia = media;
}
#Override
public void run()
{
int position = currentMedia.getPosition();
int displayTime = currentMedia.getDisplayTime();
boolean loopBool = currentMedia.getLoop();
if(currentMedia.getType()) {
mActivity.runOnUiThread(() -> {
mImageView.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.GONE);
try {
Picasso.get().load(assetObtainer.getFileFromAssets(requireContext(), currentMedia.getFullFileName())).into(mImageView);
} catch (IOException ignored) {}
});
if(displayTime>0) {
try {
sleep(displayTime);
if(!loopBool) multimedias.remove(currentMedia);
if(position<multimedias.size()-1) {
getPlayer(position);
} else getPlayer(0);
} catch (InterruptedException e) {
mActivity.runOnUiThread(() -> mImageView.setVisibility(View.GONE));
interrupt();
}
}
} else {
mActivity.runOnUiThread(() -> {
mVideoView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.GONE);
try {
mVideoView.setVideoURI(Uri.fromFile(assetObtainer.getFileFromAssets(requireContext(), currentMedia.getFullFileName())));
} catch (IOException ignored) {}
if(loopBool && multimedias.size()==1) mVideoView.setOnCompletionListener(v->getPlayer(position-1));
mVideoView.start();
});
}
}
}
#Override
public void onPause()
{
if(mPlayThread!=null){
mPlayThread.interrupt();
}
super.onPause();
}
}
bed for the fragment in the activity .xml file :
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/layout_multimedia"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/active_instructions" />
and the .xml file of the fragment :
<androidx.constraintlayout.widget.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="wrap_content"
tools:context=".tutorial.mediaplayer.MultimediaPlayer">
<VideoView
android:id="#+id/video_embed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/image_embed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="#string/image_default_no_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
As mentioned, video works super fine, but images won't show. I'd appreciate even monkey wrench suggestions before I have to rework this entirely.
As mentioned in my other answer, it's not related to the ImageView, it's a problem with SQLite and loading a boolean from the database. The default false which I wanted to correspond with video types is default, so it worked, but since it doesn't default to true the image condition wasn't fulfilled. Anyway the ImageView will work once it is actually ran.

Recyclerview disappers when EditText not in Focus

I am making an android app, it has an editText on the top and a recyclerview on the buttom.
Every time when a user hit "enter" in the editText field, I will then make an API call with OKHttp and save the info I needed to a Singleton Class so my adapter can use it and update the list.
However, the recyclerview is not showing(updating) if the focus is not in the editText
"V/ViewRootImpl: The specified message queue synchronization barrier token has not been posted or has already been removed"
It's showing this everytime when I click on the editText, I googled it and found out it might be the thread issues. The only place I interact with thread is the API request I made with okHTTP. However, the request returns and interpret the json file correctly.
public Boolean makeCall (String parameter) throws IOException, UnirestException {
// make the api call
String completedLink = BASE_LINK + parameter;
Future<HttpResponse<com.mashape.unirest.http.JsonNode>> response = Unirest.get("https://mashape-community-urban-dictionary.p.rapidapi.com/define?term=wat")
.header("x-rapidapi-host", "mashape-community-urban-dictionary.p.rapidapi.com")
.header("x-rapidapi-key", "xxxxx")
.asJsonAsync(new Callback<com.mashape.unirest.http.JsonNode>() {
#Override
public void completed(HttpResponse<com.mashape.unirest.http.JsonNode> response) {
// decode the response body
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = null;
try {
node = objectMapper.readTree(String.valueOf(response.getBody()));
} catch (JsonProcessingException ex) {
ex.printStackTrace();
}
JsonNode resultNode = node.get("list");
List<WordItem> resultList = null;
try {
resultList = objectMapper.readValue(resultNode.toString(),
new TypeReference<List<WordItem>>() {});
} catch (JsonProcessingException ex) {
ex.printStackTrace();
}
SavedInfo.getInstance().setResult(resultList);
}
#Override
public void failed(UnirestException e) {
}
#Override
public void cancelled() {
}
});
return true;
}
Every time when I hit enter on the soft keyboard, my code will start making the API call
mResult.setAdapter(adapter);
mResult.setNestedScrollingEnabled(false);
mResult.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
mResult.setLayoutManager(new LinearLayoutManager(getContext()));
mSearchBox.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
if (i == EditorInfo.IME_ACTION_DONE) {
// show progress bar
hideProgressBar(false, mProgress);
mHomeText.setText(textView.getText());
try {
if (new HttpRequest().makeCall(textView.getText().toString())){
adapter.updateList();
}
else {
// showing error message
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
hideProgressBar(true, mProgress);
// hide keyword when enter key is detected
View view = getActivity().getCurrentFocus();
if (view != null) {
InputMethodManager imm = (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
return true;
}
});
Also, one thing to point out is the Progress Bar(I set it to "VISIBLE" but not showing still)is not showing at all. I set a few places throughout my code, the Singleton Class and Http Requests are correct and return + save correct info.
Here is my updateList() in my adapter class just in case this helps
public void updateList () {
list.clear();
list.addAll(SavedInfo.getInstance().getResult());
notifyDataSetChanged();
}
Here is the my layout file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".ui.home.HomeFragment"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#F3F3F3"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="#+id/title_word_home"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="36dp"
android:fontFamily="sans-serif"
android:text="#string/home_text"
android:textAlignment="center"
android:textSize="28sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="0dp" />
<EditText
android:id="#+id/searchBox"
android:layout_width="254dp"
android:layout_height="50dp"
android:layout_marginStart="239dp"
android:layout_marginEnd="239dp"
android:autofillHints=""
android:drawableStart="#drawable/ic_search"
android:drawablePadding="10dp"
android:ems="10"
android:hint="#string/searchBox_hint"
android:imeOptions="actionDone"
android:inputType="text"
android:pointerIcon="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_word_home" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="#+id/loading_progress"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center|center_vertical"
android:indeterminate="true"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/home_resultList"
android:layout_width="409dp"
android:layout_height="wrap_content"
android:visibility="visible" />
</LinearLayout>
</LinearLayout>
Thank you for reading!
The problem occurs because the data posts to recycler view before that has downloaded. You are trying to update the recycler view in the method updateList() which called after the request started but before it completed. That's also why the progress bar doesn't show - it's immediately gone.
In order to fix the problem, you have to trigger updateList() after the data has been downloaded. The good way will be to rewrite the makeCall() method, which accepts the second argument as a callback, so you can update the list.
Define the custom callback:
interface MyCallback {
void completed(List<WordItem> result);
void error(UnirestException error);
}
Rewrite making call a bit:
public void makeCall(String parameter, MyCallback callback) {
// make the api call
String completedLink = BASE_LINK + parameter;
Future<HttpResponse<com.mashape.unirest.http.JsonNode>> response = Unirest.get("https://mashape-community-urban-dictionary.p.rapidapi.com/define?term=wat")
.header("x-rapidapi-host", "mashape-community-urban-dictionary.p.rapidapi.com")
.header("x-rapidapi-key", "xxxxx")
.asJsonAsync(new Callback<com.mashape.unirest.http.JsonNode>() {
#Override
public void completed(HttpResponse<com.mashape.unirest.http.JsonNode> response) {
// decode the response body
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = null;
try {
node = objectMapper.readTree(String.valueOf(response.getBody()));
} catch (JsonProcessingException ex) {
ex.printStackTrace();
}
JsonNode resultNode = node.get("list");
List<WordItem> resultList = null;
try {
resultList = objectMapper.readValue(resultNode.toString(),
new TypeReference<List<WordItem>>() {});
} catch (JsonProcessingException ex) {
ex.printStackTrace();
}
// SavedInfo.getInstance().setResult(resultList);
callback.completed(resultList);
}
#Override
public void failed(UnirestException e) {
callback.failed(e);
}
#Override
public void cancelled() {
}
});
}
And finally pass the callback to makeCall() and refresh the list:
#Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
if (i == EditorInfo.IME_ACTION_DONE) {
// show progress bar
hideProgressBar(false, mProgress);
// clear your list before call if you wish
// updateList(null);
mHomeText.setText(textView.getText());
new HttpRequest().makeCall(textView.getText().toString(), new MyCallback() {
#Override
public void completed(List<WordItem> result) {
updateList(result);
hideProgressBar(true, mProgress);
}
#Override
public void error(UnirestException error) {
hideProgressBar(true, mProgress);
// showing error message
}
});
// hide keyword when enter key is detected
View view = getActivity().getCurrentFocus();
if (view != null) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
return true;
}
This method should be rewrited as well:
public void updateList(List<WordItem> result) {
list.clear();
if (result != null) list.addAll(result);
notifyDataSetChanged();
}
After that problem should go away and also you have no longer need in the singleton class that holding your data.
By the way, there is the Retrofit library which used in order to make api requests in an easier way. The retrofit is the best practice in the android community, so I suggest to try use it.

Show progress while loading WebViews inside RecyclerView

In my android activity I am using a RecyclerView which contains number of MathViews. MathView is a third-party library which shows LaTeX contents (This
is somewhat similar to WebView. Implementation of MathView can be seen on this android project. github.com/lingarajsankaravelu/Katex).
The problem is, for rendering the content of this MathView, it takes a little bit longer time. As I have used few MathView components inside a RecycleView and the rendering time increases more. Therefore when the Activity started, at first in the view, some white space are shown for few seconds and then the relevant content is rendered.
As a solution for this problem, I need to show a progress bar until all the layout content of the Activity is completely rendered and after rendering is completed show up the Activity.
The relavent source codes are shown below.
MathView;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:auto="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:id="#+id/equation_item"
android:clickable="true"
android:foreground="?attr/selectableItemBackground">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp">
<katex.hourglass.in.mathlib.MathView
android:id="#+id/math_view"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="left"
app:setClickable="true"
app:setTextColor="#color/colorPrimary"
app:setTextSize="10sp"
/>
</android.support.v7.widget.CardView>
<include layout="#layout/item_divider"/>
</LinearLayout>
Recycler View;
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView 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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.a37moons.mathcheatsheets.ContentActivity"
tools:showIn="#layout/activity_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view_equations"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.NestedScrollView>
ViewHolder Class
public class ViewHolder extends RecyclerView.ViewHolder {
public MathView mathView;
public ViewHolder(View itemView) {
super(itemView);
mathView = itemView.findViewById(R.id.math_view);
}
}
Recycler Adapter Class
public class RecyclerAdapterEquations extends RecyclerView.Adapter<ViewHolder>{
private List<Equation> equations;
View view;
public RecyclerAdapterEquations(List<Equation> equations){
this.equations = equations;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.equation_view_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Equation sampleEquation = equations.get(position);
holder.mathView.setDisplayText(sampleEquation.equationString);
// holder.mathView2.setText(sampleEquation.equationString);
Log.d("MATH_APP","position "+position+" mathview text set ");
}
#Override
public int getItemCount() {
return equations.size();
}
}
Finally the implementation.
RecyclerView recyclerView;
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(new RecyclerAdapterEquations(sample));
With the help of the answer of written by azizbekian, I found the answer to my question.
As mentioned in that answer, this is the procedure;
Introduce a ProgressBar or something similar inside xml file. This view should be declared after RecyclerView in order to be
drawn on top of RecyclerView
Make RecyclerView invisible (via android:visibility="invisible")
Now RecyclerView will be actually laid out but not shown on the screen. You need a callback, that would be executed some time
later when RecyclerView is already setup. Within this callback you
will hide progress bar and change visibility of RecyclerView to
View.VISIBLE.
Now as the katex.hourglass.in.mathlib.MathView is a subclass of android WebView, we can set a WebChromeClient to this. Then, we can get the percentage of progress of loading the content.
int loadedPercentage = 0;
boolean loaded = false;
mathView.setWebChromeClient(new WebChromeClient(){
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
loadedPercentage = newProgress;
if(newProgress==100) {
//When the loading is 100% completed; todo
loaded = true;
Toast.makeText(getContext(), newProgress + "LOADED COMPLETELY", Toast.LENGTH_SHORT).show();
}
}
});
We can implement a progress bar to show the loadedPercentage. When the loadedPercentage is 100, we can see the relavent content is completely loaded.
So I editted the MathView class as follows;
public class MyMathView extends WebView {
private String TAG = "KhanAcademyKatexView";
private static final float default_text_size = 18;
private String display_text;
private int text_color;
private int text_size;
private boolean clickable = false;
private boolean loaded = false;
private int loadedPercentage = 0;
public MyMathView(Context context) {
//...
}
public MyMathView(Context context, AttributeSet attrs) {
//...
}
public boolean isLoaded(){
return loaded;
}
public int getLoadedPercentage() {
return loadedPercentage;
}
public void setViewBackgroundColor(int color)
{
//...
}
private void pixelSizeConversion(float dimension) {
//...
}
private void configurationSettingWebView()
{
//...
}
public void setDisplayText(String formula_text) {
this.display_text = formula_text;
loadData();
}
private String getOfflineKatexConfig()
{
String offline_config = "<!DOCTYPE html>\n" +
"<html>\n" +
" <head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Auto-render test</title>\n" +
" <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/katex/katex.min.css\">\n" +
" <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/themes/style.css\">\n" +
" <script type=\"text/javascript\" src=\"file:///android_asset/katex/katex.min.js\"></script>\n" +
" <script type=\"text/javascript\" src=\"file:///android_asset/katex/contrib/auto-render.min.js\"></script>\n" +
" <style type='text/css'>"+
"body {"+
"margin: 0px;"+
"padding: 0px;"+
"font-size:" +this.text_size+"px;"+
"color:"+getHexColor(this.text_color)+";"+
" }"+
" </style>"+
" </head>\n" +
" <body>\n" +
" {formula}\n" +
" <script>\n" +
" renderMathInElement(\n" +
" document.body\n" +
" );\n" +
" </script>\n" +
" </body>\n" +
"</html>";
String start = "<html><head><meta http-equiv='Content-Type' content='text/html' charset='UTF-8' /><style> body {"+
" white-space: nowrap;}</style></head><body>";
String end = "</body></html>";
//return start+offline_config.replace("{formula}",this.display_text)+end;
return offline_config.replace("{formula}",this.display_text);
}
private void loadData()
{
if (this.display_text!=null)
{
loadedPercentage = 0;
loaded = false;
this.setWebChromeClient(new WebChromeClient(){
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
loadedPercentage = newProgress;
if(newProgress==100) {
loaded = true;
Toast.makeText(getContext(), newProgress + "LOADED", Toast.LENGTH_SHORT).show();
}
}
});
this.loadDataWithBaseURL("null",getOfflineKatexConfig(),"text/html","UTF-8","about:blank");
}
}
public void setTextSize(int size)
{
//...
}
public void setTextColor(int color)
{
//...
}
private String getHexColor(int intColor)
{
//...
}
private void setDefaultTextColor(Context context) {
//...
}
private void setDefaultTextSize() {
//...
}
}
I cannot see the question itself in the description, thus I will refer to your statement in comments section:
I just need a solution to show a progress bar until all the layout content of the Activity is completely rendered and then after rendering is completed, show up the Activity.
Introduce a ProgressBar or something similar inside xml file. This view should be declared after RecyclerView in order to be drawn on top of RecyclerView
Make RecyclerView invisible (via android:visibility="invisible")
Now RecyclerView will be actually laid out but not shown on the screen. You need a callback, that would be executed some time later when RecyclerView is already setup. Within this callback you will hide progress bar and change visibility of RecyclerView to View.VISIBLE.
The problem boils to attaching "children of RecyclerView are initialized" listener.
As long as katex.hourglass.in.mathlib.MathView is a subclass of android.webkit.WebView, that means, that in order to get notified about load finished event you should register WebViewClient in following approach:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Equation sampleEquation = equations.get(position);
holder.mathView.setDisplayText(sampleEquation.equationString);
holder.mathView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// No longer interested in upcoming events - unregister
view.setWebViewClient(null);
// Now data is loaded, can make RecyclerView visible
}
});
...
}
Note, this will make RecyclerView to be shown as soon as any of the MathViews is loaded. You can wait all of them to be loaded depending on your requirement.
Don't call
setContentView(layout);
as usual, load data in adapter first, thn call
setContentView(layout);
After that set adapter to recyclerview .

Android viewPager out of memory using local html

I'm developing an Android app using Xamarin studio, but this is not important.
I use the pageViewer to upload the fragment (12 more or less). To the one display fragment i use a webView to display a local html page and with a swipe to the left, the webview content change to the next one.
So, at the fifth frgament memory problems started even if i used different tasks in my code.
My question is: Is there a way to 'detach' the fragment when i'm not displaying them? Can they not remain into my memory?
Thanks for all
This is my code, N.B: Java answers are accepeted as well
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.BookView);
_loader = ProgressDialog.Show (this, "Loading...", "Please wait...", true);
//return chapters count
chapters = 12 //example
//var dp = (int)Resources.DisplayMetrics.Density;
_layout = FindViewById<DrawerLayout> (Resource.Id.drawer_layout);
_view = FindViewById<ViewPager>(Resource.Id.bView);
_view.SetBackgroundColor (Color.White);
_currentAdapter = new AwesomeFragmentAdapter (SupportFragmentManager, path, name, chapters, this, _view);
_view.Adapter = _currentAdapter;
_view.OffscreenPageLimit = chapters;
List<int> positions = new List<int> ();
_view.PageSelected += (object sender, ViewPager.PageSelectedEventArgs e) => {
//get details
var page_load = new Task (() => {
//return an object with the chapter details
_chap = object;
});
page_load.Start();
//find the webview
_web = (WebView)_view.FindViewWithTag(300 + e.Position);
WebSettings setting = _web.Settings;
setting.CacheMode = CacheModes.Default;
setting.JavaScriptEnabled = true;
setting.BuiltInZoomControls = true;
setting.DisplayZoomControls = false;
setting.PluginsEnabled = true;
setting.SetPluginState(WebSettings.PluginState.On);
//setting.JavaScriptCanOpenWindowsAutomatically = true;
if (positions.Contains(e.Position)) {
_web.ClearCache(true);
_web.ClearView();
}
//Start when the scroll is finished
_view.PageScrollStateChanged += (object sendero, ViewPager.PageScrollStateChangedEventArgs ex) => {
if (ex.State == 0 ) {
if (positions.Contains(e.Position)) {
//_web.Reload(); --> doesn't work
//Doesn't reload the .js animations
_web.LoadUrl ("file://" + path + "/" + _chap.Name);
} else {
_web.LoadUrl ("file://" + path + "/" + _chap.Name);
positions.Add(e.Position);
}
}
};
};
}
public class BWebClient : WebViewClient
{
int _position;
string _path;
Activity _parent;
ViewPager _pager;
string _chapName;
public BWebClient (int position, string Path, Activity Parent, ViewPager Pager, string ChapName){
_position = position;
_parent = Parent;
_path = Path;
_pager = Pager;
_chapName = ChapName;
}
public override void OnPageFinished (WebView view, string url)
{
base.OnPageFinished (view, url);
view.ScrollTo (0, _position);
}
public override bool ShouldOverrideUrlLoading (WebView view, string url)
{
if (url.StartsWith ("navigate")) {
string destination = url.Substring (url.IndexOf ("navigate://") + "navigate://".Length);
int DestinationChapter = Int32.Parse (destination.Substring (0, destination.IndexOf("_")));
int l = destination.IndexOf("_") + 1;
int b = destination.Length - l;
int DestinationPage = Int32.Parse (destination.Substring (l,b));
if (DestinationPage == 0) {
_pager.SetCurrentItem(DestinationChapter ,true);
WebView _web = (WebView)_pager.FindViewWithTag(300 + DestinationChapter);
_web.LoadUrl ("file://" + _path + "/" + _chapName);
}
} else if (url.StartsWith ("pdf")) {
string file_path = System.IO.Path.Combine (_path, url.Substring (url.IndexOf ("pdf://") + "pdf://".Length));
Android.Net.Uri pdfFile = Android.Net.Uri.FromFile (new Java.IO.File (file_path));
Intent pdfIntent = new Intent (Intent.ActionView);
pdfIntent.SetDataAndType (pdfFile, "application/pdf");
_parent.StartActivity (pdfIntent);
}
return true;
}
}
public class AwesomeFragmentAdapter : FragmentPagerAdapter
{
string _path;
string _filename;
int _chapters;
Activity _parent;
FileUtilities _fUtils;
ViewPager _pager;
public AwesomeFragmentAdapter (Android.Support.V4.App.FragmentManager fm,
string path,
string filename,
int chapters,
Activity parent,
FileUtilities FUtils,
ViewPager Pagers): base(fm)
{
_path = path;
_filename = filename;
_chapters = chapters;
_parent = parent;
_fUtils = FUtils;
_pager = Pagers;
}
public override int Count
{
/* --- return chapter count --- */
get { return _chapters;}
}
public override Android.Support.V4.App.Fragment GetItem(int _position)
{
/* --- get specific item --- */
return new AwesomeFragment (_path, _filename, _position, _parent, _fUtils, _pager);
}
}
public class AwesomeFragment : Android.Support.V4.App.Fragment
{
string _path;
WebView web_view;
string _filename;
int _position;
Activity _parent;
BanjiChapter _chap;
FileUtilities _fUtils;
ViewPager _pager;
public AwesomeFragment () {}
public AwesomeFragment (string path,
string filename,
int position,
Activity parent,
FileUtilities FUtils,
ViewPager Pager)
{
_path = path;
_filename = filename;
_position = position;
_parent = parent;
_fUtils = FUtils;
_pager = Pager;
}
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
/* --- Create the view --- */
var view = inflater.Inflate (Resource.Layout.BookWebView, container, false);
//return the chapter
_chap = _fUtils.ReturnChapterDetails(_filename, _position);
web_view = view.FindViewById<WebView> (Resource.Id.webview);
web_view.SetWebViewClient(new BanjiWebClient(_position,_path,_parent, _pager, _chap.Name ));
web_view.SetBackgroundColor(Color.Transparent);
web_view.Settings.JavaScriptEnabled = true;
web_view.Tag = 300 + _position;
switch(Resources.DisplayMetrics.DensityDpi){
case Android.Util.DisplayMetricsDensity.Medium:
{
web_view.SetLayerType (LayerType.Software, null);
break;
}
case Android.Util.DisplayMetricsDensity.High:
{
web_view.SetLayerType (LayerType.Hardware, null);
break;
}
case Android.Util.DisplayMetricsDensity.Xhigh:
{
web_view.SetLayerType (LayerType.Hardware, null);
break;
}
}
if (_chap.Background == null) {
view.SetBackgroundColor (Color.White);
} else {
view.SetBackgroundDrawable (new BitmapDrawable (BitmapFactory.DecodeByteArray (_chap.Background, 0, _chap.Background.Length)));
}
if (_position == 0) {
web_view.LoadUrl ("file://" + _path + "/" + _chap.Name);
}
return view;
}
public BChapter GetCurrentBChapter()
{
return _chap;
}
}
EDIT:
BookView.axml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffececec">
<!-- The main content view -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainView">
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/bookView" />
<ImageButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="left|center"
android:background="#null"
android:id="#+id/menuButton"
/>
</FrameLayout>
<!-- The navigation drawer -->
<LinearLayout
android:id="#+id/left_menu"
android:layout_width="250dp"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:layout_gravity="start"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#111">
<Button
android:id="#+id/backStep"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="#style/button_text"
android:background="#ff000000"
android:fitsSystemWindows="false" />
<Space
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/screllArea">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ThumbLayout" />
</ScrollView>
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
BookWebView
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Use a FragmentStatePagerAdapter instead; it is designed to minimise memory overhead by possibly destroying the fragment when it is not visible, saving only the state information of that fragment.
From the developers docs:
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.
See here for documentation.

Categories