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?
Related
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.
I'm using Paging Library to load data from network using ItemKeyedDataSource. After fetching items user can edit them, this updates are done inside in Memory cache (no database like Room is used).
Now since the PagedList itself cannot be updated (discussed here) I have to recreate PagedList and pass it to the PagedListAdapter.
The update itself is no problem but after updating the recyclerView with the new PagedList, the list jumps to the beginning of the list destroying previous scroll position. Is there anyway to update PagedList while keeping scroll position (like how it works with Room)?
DataSource is implemented this way:
public class MentionKeyedDataSource extends ItemKeyedDataSource<Long, Mention> {
private Repository repository;
...
private List<Mention> cachedItems;
public MentionKeyedDataSource(Repository repository, ..., List<Mention> cachedItems){
super();
this.repository = repository;
this.teamId = teamId;
this.inboxId = inboxId;
this.filter = filter;
this.cachedItems = new ArrayList<>(cachedItems);
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Long> params, final #NonNull ItemKeyedDataSource.LoadInitialCallback<Mention> callback) {
Observable.just(cachedItems)
.filter(() -> return cachedItems != null && !cachedItems.isEmpty())
.switchIfEmpty(repository.getItems(..., params.requestedLoadSize).map(...))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
#Override
public void loadAfter(#NonNull LoadParams<Long> params, final #NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
repository.getOlderItems(..., params.key, params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
#Override
public void loadBefore(#NonNull LoadParams<Long> params, final #NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
repository.getNewerItems(..., params.key, params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
#NonNull
#Override
public Long getKey(#NonNull Mention item) {
return item.id;
}
}
The PagedList created like this:
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setInitialLoadSizeHint(preFetchedItems != null && !preFetchedItems.isEmpty()
? preFetchedItems.size()
: PAGE_SIZE * 2
).build();
pagedMentionsList = new PagedList.Builder<>(new MentionKeyedDataSource(mRepository, team.id, inbox.id, mCurrentFilter, preFetchedItems)
, config)
.setFetchExecutor(ApplicationThreadPool.getBackgroundThreadExecutor())
.setNotifyExecutor(ApplicationThreadPool.getUIThreadExecutor())
.build();
The PagedListAdapter is created like this:
public class ItemAdapter extends PagedListAdapter<Item, ItemAdapter.ItemHolder> { //Adapter from google guide, Nothing special here.. }
mAdapter = new ItemAdapter(new DiffUtil.ItemCallback<Mention>() {
#Override
public boolean areItemsTheSame(Item oldItem, Item newItem) {
return oldItem.id == newItem.id;
}
#Override
public boolean areContentsTheSame(Item oldItem, Item newItem) {
return oldItem.equals(newItem);
}
});
, and updated like this:
mAdapter.submitList(pagedList);
P.S: If there is a better way to update list items without using Room please share.
All you need to do is to invalidate current DataSource each time you update your data.
What I would do:
move networking logic from MentionKeyedDataSource to new class that extends PagedList.BoundaryCallback and set it when building your PagedList.
move all you data to some DataProvider that holds all your downloaded data and has reference to DataSource. Each time data is updated in DataProvider invalidate current DataSource
Something like that maybe
val dataProvider = PagedDataProvider()
val dataSourceFactory = InMemoryDataSourceFactory(dataProvider = dataProvider)
where
class PagedDataProvider : DataProvider<Int, DataRow> {
private val dataCache = mutableMapOf<Int, List<DataRow>>()
override val sourceLiveData = MutableLiveData<DataSource<Int, DataRow>>()
//implement data add/remove/update here
//and after each modification call
//sourceLiveData.value?.invalidate()
}
and
class InMemoryDataSourceFactory<Key, Value>(
private val dataProvider: DataProvider<Key, Value>
) : DataSource.Factory<Key, Value>() {
override fun create(): DataSource<Key, Value> {
val source = InMemoryDataSource(dataProvider = dataProvider)
dataProvider.sourceLiveData.postValue(source)
return source
}
}
This approach is very similar to what Room does - every time table row is updated - current DataSource is invalidated and DataSourceFactory creates new data source.
You can modify directly in your adapter if you called currentList like that
class ItemsAdapter(): PagedListAdapter<Item, ItemsAdapter.ViewHolder(ITEMS_COMPARATOR) {
...
fun changeItem(position: Int,newData:String) {
currentList?.get(position)?.data = newData
notifyItemChanged(position)
}
}
My question is how to display more than one user interface screen per program. I'm sure this question has been asked before, but I haven't found a solution that works for me (or should I say that I understand). There isn't anything exotic about the scenarios I'm talking about. The first is simply validating inputs from a screen and re-displaying the same screen in the case of errors.
I'll pose the question in terms of the second more complicated scenario: displaying an input data screen, processing the inputs; and then displaying the outputs. This complicated somewhat by the fact that the first, a simple screen with 5 text boxes and a command button, uses an FXML file, whereas the second, a multi-select list box does not. The flow is:
1. Main program calls
2. A loader program which loads the FXML and somehow or another calls
3. A controller which receives the inputs and processes them to produce output.
The final step is to display the output in the form of a multi-select list box. Note that the first GUI employs a controller, which is a separate file, to process the inputs, whereas the second uses an event handler, which is in the same file as the screen definition, to make the selection(s) when the user clicks a command button.
Various SO posts have said that the way to go is to not shut down the application once the first GUI has completed via but Keep the JavaFX run time going in the background with
Platform.setImplicitExit(false);
and to define each GUI and simply switch scenes to the one you want to display. But where, given the scenario I described do you put the code? The second GUI has three pieces: screen definition, event handler(s), and scene switching code. Where do you put each? #2 or #3. If you put some in #2 and some in #3, how does #3 know what you did in #2?
The code for #2 the FMXL loader:
public class inputData extends Application {
public static void load() {
launch();
}
#Override
public void start(Stage stage) throws Exception {
GridPane inpRoot = FXMLLoader.load(getClass().getResource("inputData.fxml"));
Scene inpScene = new Scene(inpRoot, 300, 275);
stage.setTitle("Amsnag 2.1 - Query Input");
stage.setScene(inpScene);
stage.show();
}
}
Code for #3, the list box definition and handlers, which worked fine running separately. It's only when I tried to incorporate it with the rest of the program that it failed.
public class multiList extends Application {
public static void load() {
launch();
}
public static final ObservableList options = FXCollections.observableArrayList();
#Override
public void start(final Stage stage) {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// lcreate temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
} ); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
layout.getChildren().setAll(listView, controls);
layout.setPrefWidth(320);``enter code here
Scene scene = new Scene(layout);
// stage.setScene(new Scene(layout));
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
public static void main(String[] args) { launch(args); }
}
You can't reuse an Application subclass in a different application.
The Application class represents an entire application, or perhaps more specifically its lifecycle. So it has methods such as init(), start(), and stop() which are invoked by the FX Application Toolkit at the appropriate moments in the lifecycle of the application.
The layout for your multiList (aside: please use proper naming conventions) class is performed in the start() method, so it can only happen at the start of the application. By putting the layout code here, you make it impossible to reuse so that it is performed at a later point in a different application.
So move the layout for MultiList to a separate class:
public class MultiList {
public static final ObservableList options = FXCollections.observableArrayList();
private final VBox view ;
public MultiList() {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// create temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
} catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
}); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
view = new VBox(10);
view.setAlignment(Pos.CENTER);
view.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
view.getChildren().setAll(listView, controls);
view.setPrefWidth(320);
}
public Parent getView() {
return view ;
}
}
Now if you want to test this out on its own, you can write an application for it:
public class MultiListApp extends Application {
#Override
public void start(Stage primaryStage) {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
primaryStage.setScene(scene);
primarStage.setTitle("Select one or more options");
primaryStage.show();
}
}
Or in the controller class for InputData.fxml, you can do the same thing:
public class InputDataController {
#FXML
private void someEventHandler() {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
}
I'm trying to implement Google Play Service Location APIs to use localization in my APP.
protected void createLocationRequest() {
// Create an instance of GoogleAPIClient.
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest);
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
final LocationSettingsStates states = result.getLocationSettingsStates();
}
I suppose to already import all needed libraries, anyway AndroidStudio notify to 'Cannot resolve method getLocationSettingsStates()'.
I does not get other error.
I can't figure out about this.
result is a PendingResult<LocationSettingsResult>, not a LocationSettingsResult itself which implements getLocationSettingsStates(). You need to do something like:
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates states = result.getLocationSettingsStates();
// Call states.isBlePresent(), etc.
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.