Why did I need to use a notifyDataSetChanged() when I instantiated my adapter after loading in my model? - android-volley

Goal: import a JSON using volley and setting up a recycler view to display a long list of pokemon that I've parsed from the JSON (I'm making a pokedex).
Summary of code: custom adapter (called PokemonAdapter) but it's really doing normal adapter things; it's just specifying how to inflate a new view and what text to set. My MainActivity is where I was having trouble. In onCreate, I first loaded my JSON dataset, then set the adapter object to a new PokemonAdapter. My code compiled, didn't produce errors at run time, but also didn't produce a list. That's when I learned about the notifyDataSetChanged() method. I didn't see why it would matter but I also didn't see why it would hurt, so I tried it and it worked.
I'm a little confused. I was wondering if someone could explain why I needed to update my adapter even though I set the adapter after loading my data. Is it because I initially declare the adapter above the load method usage? I'm new to OOP so I get a little confused with declaration vs instantiation.
public class MainActivity extends AppCompatActivity {
private List<Pokemon> pokemonDB = new ArrayList<>();
private RequestQueue queue;
/** RECYCLER VIEW */
/* Obtain handles for Recycler View components*/
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
/** METHODS */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadPokemon();
/* Setting up the Recycler View*/
// Link it to XML doc to inflate recycler object
recyclerView = findViewById(R.id.recycler_view);
// initialize layout manager and use setter method
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// Initialize a new adapter using the PokemonAdapter class and use setter method
adapter = new PokemonAdapter(pokemonDB);
recyclerView.setAdapter(adapter);
}
/* Load JSON from Poke API using volley protocol */
public void loadPokemon() {
//Instantiate the RequestQueue
queue = Volley.newRequestQueue(this);
String url = "https://pokeapi.co/api/v2/pokemon/?limit=151";
// request a JSON response from the provided URL
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonResultsArray = response.getJSONArray("results");
for (int i = 0; i < jsonResultsArray.length(); i++) {
JSONObject pokemonResult = jsonResultsArray.getJSONObject(i);
String pokemonName = pokemonResult.getString("name");
String pokemonUrl = pokemonResult.getString("url");
// Now, add this data to a pokemon object in the pokemonDB array list
pokemonDB.add(new Pokemon(pokemonName, pokemonUrl));
}
//this notifies the adapter that the data has changed
adapter.notifyDataSetChanged();
}
catch (JSONException e) {
Log.e("cs50", "Error parsing JSON pokedex objects.");
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("cs50", "error retrieving JSON pokedex database.");
}
}
);
// Add the request to the queue
queue.add(jsonObjectRequest);
}
}

Loading data may take a while. So the recycler view adapter is probably set before the data is downloaded and there are no items to show. So we have to use the notifyDataSetChanged() method inside the response listener after we get all the data in order to update the recycler view.

Related

Pushing Images Vaadin Java

i am trying to create a turn-base card game in Vaadin-Java, everything was going well so far, but i have a problem with pushing Vaadin Images to other UI. I did copy Broadcast/BroadcasterView Class from Vaadin Documentation and it works as intended, but not for images.
public class Broadcaster {
static Executor executor = Executors.newSingleThreadExecutor();
static LinkedList<Consumer<String>> listeners = new LinkedList<>();
public static synchronized Registration register(
Consumer<String> listener) {
listeners.add(listener);
return () -> {
synchronized (Broadcaster.class) {
listeners.remove(listener);
}
};
}
public static synchronized void broadcast(String message) {
for (Consumer<String> listener : listeners) {
executor.execute(() -> listener.accept(message));
}
}
}
#Push
#Route("broadcaster")
public class BroadcasterView extends Div {
VerticalLayout messages = new VerticalLayout();
Registration broadcasterRegistration;
// Creating the UI shown separately
#Override
protected void onAttach(AttachEvent attachEvent) {
UI ui = attachEvent.getUI();
broadcasterRegistration = Broadcaster.register(newMessage -> {
ui.access(() -> messages.add(new Span(newMessage)));
});
}
#Override
protected void onDetach(DetachEvent detachEvent) {
broadcasterRegistration.remove();
broadcasterRegistration = null;
}
}
public BroadcasterView() {
TextField message = new TextField();
Button send = new Button("Send", e -> {
Broadcaster.broadcast(message.getValue());
message.setValue("");
});
HorizontalLayout sendBar = new HorizontalLayout(message, send);
add(sendBar, messages);
}
the code above works fine for Strings, Vaadin Icons etc, but when i replace for and naturally change the broadcast method, there is no reaction.
i've searched for the solution throughout the internet, but it seems, people don't need to push images or it's simply not possible here. I thought that this is perhaps the matter of payload, but it doesn't work even for 5px x 5px images
perhaps one of You have encountered such problem and found solution?
You need to pass data through the broadcaster, but what you write about your attempts makes me suspect that you've been trying to pass UI components (i.e. instances of com.vaadin.flow.component.html.Image). That won't work because a UI component instance cannot be attached to multiple locations (i.e. multiple browser windows in this case) at the same time.
What you can try is to pass the data (e.g. a String with the image URL) through the broadcaster and then let each subscriber create their own Image component based on the data that they receive.

Xamarin Android Share Link/Text via social media from custom renderer

I wan't to share a link via social media from custom renderer
public class CustomActions : ICustomActions
{
Context context = Android.App.Application.Context;
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
context.StartActivity(Intent.CreateChooser(sharingInt, "Share via"));
}
}
This error occur
Android.Util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
even when I added the below code I still get same error
sharingInt.AddFlags(ActivityFlags.NewTask);
The problem is that Intent.CreateChooser creates yet another Intent. What you want to do is to set the flag on this new intent:
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
var intent = Intent.CreateChooser(sharingInt, "Share via");
intent.AddFlags(ActivityFlags.NewTask);
context.StartActivity(intent);
}
Alternatively to avoid the need to do this, you could cache the MainActivity instance Xamarin.Forms uses:
public MainActivity
{
public static MainActivity Instance {get;private set;}
protected override void OnCreate(Bundle bundle)
{
Instance = this;
...
}
}
And then use the Instance as the Context in your code instead of the Application.Context

Can I create an email from a Dropwizard View using Freemarker?

I have a .ftl file for a resource that I use to display it via a RESTful endpoint, I also want to send this representation via email to users. Dropwizard does some magic with Views that allows the populated template body to be returned to my REST endpoint (I assume as a String or something fancier like a StringBuilder).
Is there a way to request the body of a View without using the rest client? I don't want to make my email representation dependent on the HTML used in the REST client either, so no suggestions to just route the email to wget the contents of hitting an endpoint.
From looking at the dropwizard source, it seems that I will need to acquire a ViewRenderer from somewhere, potentially by using the ServiceLoader.load() - as this is how ViewBundle acquires a copy (if you don't provide any).
As pointed out in the comments, Rossiar is exactly right.
The way to achieve this is to use the same ViewRenderer that DW is using as well. One does not have to use ServiceLocator patterns because all they do is to invoke the constructor anyway.
A little example:
public class MyView extends View {
private TestPerson person;
protected MyView(TestPerson person) {
super("TestView.ftl");
this.person = person;
}
public TestPerson getPerson() {
return person;
}
public static class TestPerson {
public String getName() {
return "PandaaDb";
}
}
}
This is the view class we want to render. Note: Freemaker expects the methods to be public, otherwise it will refuse to access them.
The corresponding ftl file:
<#-- #ftlvariable name="" type="viewTest.Two.MyView" -->
<html>
<body>
<h1>Hello, ${person.name?html}</h1>
</body>
</html>
Then, in the my main, we can do:
public class ViewTestMain {
public static void main(String[] args) throws UnsupportedEncodingException {
FreemarkerViewRenderer render = new FreemarkerViewRenderer();
TestPerson p = new TestPerson();
MyView v = new MyView(p);
ByteArrayOutputStream st = new ByteArrayOutputStream();
try {
render.render(v, Locale.getDefault(), st);
} catch (IOException e) {
e.printStackTrace();
}
String string = st.toString("UTF-8");
System.out.println(string);
}
}
Or, alternatively, we can reuse the pattern DW layed out for us by querying the service locator and checking each ViewRenderer if it is applicable.
public class ViewTestMain {
public static void main(String[] args) throws UnsupportedEncodingException {
ServiceLoader<ViewRenderer> load = ServiceLoader.load(ViewRenderer.class);
TestPerson p = new TestPerson();
MyView v = new MyView(p);
ByteArrayOutputStream st = new ByteArrayOutputStream();
ViewRenderer r = null;
for(ViewRenderer vr : load) {
if(vr.isRenderable(v)) {
r = vr;
}
}
try {
r.render(v, Locale.getDefault(), st);
} catch (IOException e) {
e.printStackTrace();
}
String string = st.toString("UTF-8");
System.out.println(string);
}
}
Note, you can add your own ServiceRenderer by simply adding the implementations to the services. This is done by placing a file with the ServiceRender interface name (fully qualified) into
src/main/resources/META-INF/services
And then adding the fully qualified implementation name into that file. This will make it discoverable.
Which will print:
<html>
<body>
<h1>Hello, PandaaDb</h1>
</body>
</html>
Now,I assume that you have your DAO/Service layer split from the jersey layer. In which case, you can write a client that simply access the same model objects, instantiates the views the same way DW is usually doing it, and simply run them through the renders.
Hope that helps,
Artur

ImageLoader show image slowly in second activity

I have two activies that use UIL.
My general configuration application of UIL is:
public static void configureDefaultImageLoader(Context context) {
DisplayImageOptions thumbOptions = new DisplayImageOptions.Builder()
.showImageOnFail(R.drawable.ic_error_red_24dp)
.cacheInMemory(true)
.cacheOnDisk(true)
.displayer(new RoundedBitmapDisplayer(90))
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.considerExifParams(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.defaultDisplayImageOptions(thumbOptions)
.threadPriority(Thread.MAX_PRIORITY)
.tasksProcessingOrder(QueueProcessingType.LIFO)
.threadPoolSize(5)
.build();
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config);
}
Either activies have a custom adapter, that initialize UIL in constructor.
In my first activity the loading is fast, and anything work. The code's adapter is:
private ImageLoader imageLoader;
public ListViewAdapter(Context context)
{
layoutInflater = LayoutInflater.from(context);
imageLoader = ImageLoader.getInstance();
}
and in getView method:
if(thumbName != null ) {
imageLoader.displayImage("assets://coralsImages/" + thumbName, viewHolder.imageView);
}
The code's adapter in second activity is ugual, but loading is more slowly and difficult.How can resolve this problem?

Slow loading of layout

I have a super class which is in a library. This library take care of initializing some basic layout components and other stuff. My problem is that it takes 1.x seconds to load the layout, and shows the default layout for a while, before setting the child-specified layout.
This is the method of my super class:
public void InitializeWindow(Activity act, int layoutResourceId, String windowTitle,
Object menuAdapter, int slideMenuMode) {
super.setContentView(layoutResourceId);
super.setBehindContentView(R.layout.menu_frame);
this.menuAdapter = menuAdapter;
this.slideMenuMode = slideMenuMode;
setWindowTitle(windowTitle);
initializeSlidingMenu();
}
This is called this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.InitializeWindow(this, R.layout.activity_home, "\t\tHome",
new MenuAdapter(this, R.menu.slide_menu), SlidingMenu.TOUCHMODE_FULLSCREEN);
}
The application works like a charm, but it takes, as I said around 1.x seconds to load the layout passed from the child-class. Why does this happen?
By request, this is my initializeSlideMenu() method:
public void initializeSlidingMenu() {
this.setSlidingActionBarEnabled(true);
getSlidingMenu().setBehindOffsetRes(R.dimen.actionbar_home_width);
getSlidingMenu().setShadowWidthRes(R.dimen.shadow_width);
getSlidingMenu().setShadowDrawable(R.drawable.shadow);
getSlidingMenu().setTouchModeAbove(slideMenuMode);
getSlidingMenu().setBehindScrollScale(0.25f);
ListView v = new ListView(this);
v.setBackgroundColor(Color.parseColor("#000000"));
v.setAdapter((ListAdapter) menuAdapter);
getSlidingMenu().setMenu(v);
}
To avoid such problems there are three ways in general.
Let your onCreate() finish after setContentView() call as early as possible. You can use postDelayed runnable to delay few initialization which may not be needed at early stages.
Do some task when the view is ready, it causes the Runnable to be added to the message queue of that view.
Snippet
view.post(new Runnable() {
#Override
public void run() {
}
});
If none of the above helps consider "Optimize with stubs" link : http://android-developers.blogspot.in/2009/03/android-layout-tricks-3-optimize-with.html
Hope it helps.
I suspect that the trouble spot for you is with:
v.setAdapter((ListAdapter) menuAdapter);
You should do this as part of an AsyncTask. It will often be very slow to execute the loading by the adapter.
Here is a snippet from a sample AsyncTask implementation:
//before starting the load, I pop up some indicators that I'm doing some loading
progressBar.setVisibility(View.VISIBLE);
loadingText.setVisibility(View.INVISIBLE);
AsyncTask<Void, Void, Void> loadingTask = new AsyncTask<Void, Void, Void>() {
private ArrayList<Thing> thingArray;
#Override
protected Void doInBackground(Void... params) {
//this is a slow sql fetch and calculate for me
thingArray = MyUtility.fetchThings(inputValue);
return null;
}
#Override
public void onPostExecute(Void arg0) {
EfficientAdapter myAdapter = new EfficientAdapter(MyActivity.this, thingArray);
listView.setAdapter(myAdapter);
//after setting up my adapter, I turn off my loading indicators
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
RelativeLayout layout = (RelativeLayout)MyActivity.this.findViewById(R.id.spacey);
if (layout != null) {
LayoutInflater inflater = LayoutInflater.from(MyActivity.this);
View view = inflater.inflate(R.layout.name_tabled_sub, layout);
NamedTableView tableView = new NamedTableView(MyActivity.this, view);
}
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
};
loadingTask.execute();
You can also do "PreExecute" items with the Async task, as well as update.

Resources