I just switched to the newest version of android-maps-extensions (2.2.0) together with newest Play Services (6.5.87) and support library (21.0.3).
And now I can't reuse my MapFragment.
I have MainActivity with NavigationDrawer. One of the fragments is fragment with GoogleMap fragment inside. When I switch between fragments in NavigationDrawer they recreate themselves. Previously I used very simply solution for this. I used already inflated view:
public View onCreateView(LayoutInflater inflater, ViewGroup container, final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
parent.removeView(view);
}
return view;
}
view = inflater.inflate(R.layout.fragment_map, container, false);
SupportMapFragment mapFragment = SupportMapFragment.newInstance();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.container, mapFragment).commit();
mapFragment.getExtendedMapAsync(new OnMapReadyCallback() {
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
setupMap();
}
});
return view;
}
But now it doesn't work. Map doesn't show when this fragment opens second time.
I can just throw out this ugly code
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
parent.removeView(view);
}
return view;
}
And always recreate view with inside. But I have thousands of Markers (of course with awesome Clustering) and it takes many time to repaint them.
I know it's not the problem of extensions-library, Google Maps have the same behavior. But maybe you know right decision?
Instead of using MapFragment we can use MapView, that exists in androidmapsextensions too. And it can be reused.
Since adding MapView programatically, need to call mapView.onCreate(bundle)
private MapView mapView;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_map, container, false);
if (mapView != null) {
ViewGroup parentViewGroup = (ViewGroup) mapView.getParent();
if (parentViewGroup != null) {
parentViewGroup.removeView(mapView);
}
} else {
mapView = new MapView(getActivity());
mapView.onCreate(Bundle.EMPTY); //need if programmatically add
}
((ViewGroup)view.findViewById(R.id.container)).addView(mapView);
mapView.getExtendedMapAsync(new OnMapReadyCallback() {
public void onMapReady(GoogleMap googleMap) {
setUpMap(GoogleMap);
}
});
}
Or if we don't need any setup for reused mapView
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_map, container, false);
boolean needSetupMap = true;
if (mapView != null) {
ViewGroup parentViewGroup = (ViewGroup) mapView.getParent();
if (parentViewGroup != null) {
parentViewGroup.removeView(mapView);
needSetupMap = false;
}
} else {
mapView = new MapView(getActivity());
mapView.onCreate(Bundle.EMPTY);
}
((ViewGroup)view.findViewById(R.id.container)).addView(mapView);
if (needSetupMap) {
mapView.getExtendedMapAsync(new OnMapReadyCallback() {
public void onMapReady(GoogleMap googleMap) {
setUpMap(GoogleMap);
}
});
}
}
Related
I'm trying to take a picture from a Fragment but Activityresultlauncher won't fire, it works in Activity but I'm using Viewpager/Tablayout.
When I debugged only fire when I cancel the camera.
Please someone can Help me.
Here's my code.
public class FragmentUsuario extends Fragment {
FragmentUsuarioBinding dtb;
ActivityResultLauncher<Intent> launcherVoltAct = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
int rotation = MisFunciones.getImageOrientation(mFotoFile.getAbsolutePath());
File newImage = MisFunciones.reSaveBitmap(MisFunciones.ruta_fotos + File.separator, MisFunciones.getCode() + ".JPG", rotation, mFotoFile);
}
});
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
dtb = FragmentUsuarioBinding.inflate(inflater, container, false);
return dtb.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
dtb.fotoVoltAct.setOnClickListener(v -> takeFoto());
}
private void takeFoto() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.setPackage("net.sourceforge.opencamera");
if (cameraIntent.resolveActivity(requireActivity().getPackageManager()) != null) {
mFotoFile = MisFunciones.crearImageFile();
Uri uri = FileProvider.getUriForFile(requireContext(), requireActivity().getPackageName() + ".provider", mFotoFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
launcherVoltAct.launch(cameraIntent);
}
}
}
I'm developing the spark List with Large Images in flex mobile.
Each ItemRenderer has a large image.
like this.
<s:ItemRenderer>
...
<s:BitmapImage source="{data.bmpData}" />
</s:ItemRenderer>
In dataProvider, there is BitmapData as name "bmpData".
The problem is performance while scrolling.
While scrolling, it stopped for a while when new Image is rendered.
help me please.
If the problem is you render too many bitmapdata in same time, you can render them one by one in different frames.
Here is an example.
Make a custom ItemRenderer
class YourItemRenderer
{
override public function set data(value:Object):void
{
if (super.data != value)
{
super.data = value;
yourBitmapImage.source = null;
//when the data change, don't call the render function directly
EnterFrameManager.getInstance().addRenderFunction(render)
}
}
private function render():void
{
if (yourBitmapImage != null && data != null)
{
yourBitmapImage.source = data.bmpData;
}
}
}
EnterFrameManager is used to control the render functions.
class EnterFrameManager
{
import mx.core.FlexGlobals;
public function EnterFrameManager()
{
FlexGlobals.topLevelApplication.addEventListener( Event.EnterFrame, onEnterFrameHandler)
}
private var _instance:EnterFrameManager;
public static function getInstance():EnterFrameManager
{
if (_instance == null)
{
_instance = new EnterFrameManager();
}
return instance;
}
//save the render functions
private var renderQueue:Array = [];
private var nowIntervalFrame:int = 0;
//change it to small value when you don't feel lag
private const UPDATE_INTERVAL_FRAMES:int = 6;
private function onEnterFrameHandler(e:Event):void
{
nowIntervalFrame++;
if (nowIntervalFrame >= UPDATE_INTERVAL_FRAMES)
{
nowIntervalFrame = 0;
//change renderQueue by waitQueue
for each (var f:Function in waitQueue)
{
addFunctionToQueue(f, renderQueue);
}
waitQueue.length = 0;
if (renderQueue.length > 0)
{
var f:Function = renderQueue.shift();
f();
}
}
}
private var waitQueue:Array = [];
public function addRenderFunction(f:Function):void
{
addFunctionToQueue(f, waitQueue);
}
private function addFunctionToQueue(f:Function, queue:Function):void
{
var index:int = queue.indexOf(f);
if (index == -1)
{
queue.push(f);
}
else
{
var temp:Function = queue.splice(index, 1);
queue.push(temp);
}
}
}
I have 3 fragments inside a FragmentPagerAdapter and I want to call a web service and load data asynchronous in a Recycler View .
Everything work correctly and data recevie successfully but Recycler View show nothing at all until I swipe Fragment at least 2 position and after back to that fragment recycler view show data.
what's problem? how can I solve it?
these are my code:
ActivityMain.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
ViewPager pager = (ViewPager) findViewById(R.id.fragmentPagerMain);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowHomeEnabled(true);
FragmentNavigationDrawer fragNavDrawer = (FragmentNavigationDrawer) getSupportFragmentManager().findFragmentById(R.id. fragment_navigation_drawer);
fragNavDrawer.setUp((DrawerLayout) findViewById(R.id.drawerLayout), toolbar);
MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getSupportFragmentManager() );
pager.setAdapter(adapter);
pager.setCurrentItem(3);
SlidingTabLayout tabs = (SlidingTabLayout) findViewById(R.id.pagerTabLayout);
tabs.setViewPager(pager);
}
FragmentPagerAdapter.java
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return FragmentList.getInstance(position);
case 1:
return FragmentList.getInstance(position);
case 2:
return FragmentList.getInstance(position);
default:
return FragmentList.getInstance(position);
}
}
FragmentList.java
public class FragmentList extends Fragment {
private ViewGroup view;
private ArrayList<ModelPurchase> data = new ArrayList<>();
private MyRecyclerPurchaseList adapter;
private RecyclerView recyclerView;
public static Fragment getInstance(int catId) {
FragmentList fragment = new FragmentList();
Bundle bundle = new Bundle();
bundle.putInt("catId", catId);
fragment.setArguments(bundle);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = (ViewGroup) inflater.inflate(R.layout.fragment_list, container, false);
new HttpRequests().new PurchasedList(new HttpRequests.RequestCompeleteListener() {
#Override
public void onCompeletd(ArrayList<ModelPurchase> arrayList) {
data = arrayList;
adapter.notifyDataSetChanged();
}
}).getList();
recyclerView = (RecyclerView) view.findViewById(R.id.listAll);
adapter = new MyRecyclerPurchaseList(getActivity(), data);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return view;
}}
I had approximately the same issue. Setting an adapter to a RecyclerView needs to be done in OnCreateView. So you we need somehow to recall OnCreateView when our data has finished loading.
This can be done by detaching then reattaching the fragment, so your code will look like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = (ViewGroup) inflater.inflate(R.layout.fragment_list, container, false);
// use a boolean to prevent loading data twice...
if ( !dataIsLoaded )
{
new HttpRequests().new PurchasedList(new HttpRequests.RequestCompeleteListener() {
#Override
public void onCompeleted(ArrayList<ModelPurchase> arrayList) {
data = arrayList;
dataIsLoaded = true;
adapter.notifyDataSetChanged();
// Detach then attach the fragment
FragmentTransaction = ft getFragmentManager().beginTransaction();
ft.detach(FragmentList.this);
ft.attach(FragmentList.this);
ft.commit();
}
}).getList();
}
else
{
recyclerView = (RecyclerView) view.findViewById(R.id.listAll);
adapter = new MyRecyclerPurchaseList(getActivity(), data);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return view;
}
}}
i'm developing an application to an Android Tablet (XOOM), and i would like to know if it is possile to put the action bar on the bottom of the screen, instead of the top (which is the default option).
Thanks.
if you're getting your ActionBar in an Activity you could just add this android:uiOptions="splitActionBarWhenNarrow" to your manifest for that specific Activity.
That worked for me :)
Yes it is possible like this,try this.
public void setActionBArAtBottom(View layoutView, Intent intent,
Context context, ContentValues componentIds) throws Exception {
if (componentIds != null) {
setValues(componentIds);
} else {
System.out
.println("Provide appropriate information to set ActionBar at bottom");
try {
throw new UnsufficientResourcesException();
} catch (UnsufficientResourcesException e) {
e.printStackTrace();
}
}
// getting the parent of screen..actually this is the child of root
// parent...
ViewGroup screenParent = (ViewGroup) layoutView.getParent().getParent();
// now getting actionbarview...as follows...
View actionBArView = ((ViewGroup) screenParent.getChildAt(0))
.getChildAt(0);
// now getting framelayout that is custom layout to display actionbar at
// the bottom...
FrameLayout customActionBarFrameLayout = (FrameLayout) layoutView
.findViewById(BOTTOM_ACTION_BAR_LAYOUT_ID);
// now remove actionbarView from its parent view...
ViewGroup actionBarViewParent = removeFromParent(actionBArView);
removeTitleAndIcon((ViewGroup) actionBArView);
// now setting actionbar view to the framlayout...
customActionBarFrameLayout.addView(actionBArView);
// get the custombar layout....
View customTitleBarLayout = LayoutInflater.from(context).inflate(
NEW_TITLE_BAR_LAYOUT, null);
// setting icon to titile bar...
((ImageView) customTitleBarLayout.findViewById(NEW_TITLE_ICON__ID))
.setImageDrawable(getActivityIcon(intent, context));
// setting title...
((TextView) customTitleBarLayout.findViewById(NEW_TITLE_ID))
.setText(((SherlockActivity) context).getTitle());
// now set this layout to
actionBarViewParent.addView(customTitleBarLayout);
}
// method use to remove child from its parent...
private ViewGroup removeFromParent(View child) {
// removing child...
ViewGroup parentGroup = (ViewGroup) child.getParent();
parentGroup.removeView(child);
return parentGroup;
}
// this method will set application icon to custom title bar layout...
private Drawable getActivityIcon(Intent intent, Context context) {
// getting the current activity icon set...
Drawable drawable = null;
try {
drawable = context.getPackageManager().getActivityIcon(intent);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return drawable;
}
// remove title and icon from bottombar...
private void removeTitleAndIcon(ViewGroup actionBarViewGroup) {
actionBarViewGroup.removeAllViews();
}
private void setValues(ContentValues contentValues) throws Exception {
try {
BOTTOM_ACTION_BAR_LAYOUT_ID = contentValues
.getAsInteger(BOTTOM_ACTION_BAR_FRAME_LAYOUT_ID);
NEW_TITLE_BAR_LAYOUT = contentValues
.getAsInteger(NEW_TITLE_BAR_LAYOUT_ID);
NEW_TITLE_ICON__ID = contentValues
.getAsInteger(NEW_TITLE_ICON_IMAGEVIEW_ID);
NEW_TITLE_ID = contentValues.getAsInteger(NEW_TITLE_VIEW_ID);
} catch (Exception e) {
throw new UnsufficientResourcesException();
}
}
}
I have a simple app, in one activity I take name and date of birth. I store it in the database. and in the main activity I have linearlayout which will show all the names.
When I click on any of the name in the main activity, it should delete that name from the database and also refresh the view.
I am able to delete the entry from database, but my linear layout view is not being updated. Can some one pls help.
public class child extends Activity {
private Intent intent;
private LinearLayout layout;
private LayoutInflater linflater;
private int i =0;
private Cursor cr;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.child);
layout = (LinearLayout)findViewById(R.id.layout);
Button addBtn = (Button)findViewById(R.id.AddButton);
Button remBtn = (Button)findViewById(R.id.RemoveButton);
intent = new Intent(this,login.class);
layout = (LinearLayout) findViewById(R.id.mylayout1);
linflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//Check the database if there are any entries available. If available, then
//list them on the main screen
final myDBAdapter mydb = new myDBAdapter(getApplicationContext());
mydb.open();
cr = mydb.GetMyData();
if(cr.getCount()>0)
{
cr.moveToFirst();
for (int i=0;i<cr.getCount();i++)
{
cr.moveToPosition(i);
buildList(cr.getString(1),cr.getString(2));
}
}
//Start the login activity which will return the newly added baby name
addBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startActivityForResult(intent, 1001);
}
});
//Remove all the entries from Database
remBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(cr.getCount()>0)
{
cr.moveToFirst();
for (int i=0;i<cr.getCount();i++)
{
Toast.makeText(getApplicationContext(), cr.getString(1),
Toast.LENGTH_LONG).show();
mydb.RemoveEntry(cr.getString(1));
cr.moveToPosition(i);
}
}
}
});
mydb.close();
}
private void buildList(final String bname,String bsex)
{
final View customView = linflater.inflate(R.layout.child_view,
null);
TextView tv = (TextView) customView.findViewById(R.id.TextView01);
//tv.setId(i);
tv.setText(bname);
tv.setTextColor(getResources().getColor(R.color.black));
tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myDBAdapter mydb = new myDBAdapter(getApplicationContext());
mydb.open();
if (mydb.RemoveEntry(bname)>0)
{
Toast.makeText(getApplicationContext(), "Row deleted",
Toast.LENGTH_LONG).show();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WHAT IS REQUIRED HERE TO UPDATE THE VIEW???
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
else
{
Toast.makeText(getApplicationContext(), "Row not deleted",
Toast.LENGTH_LONG).show();
}
}
});
layout.addView(customView);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == 1001)
{
if(resultCode == RESULT_OK)
{
Bundle extras = data.getExtras();
buildList(extras.getString("bname"),extras.getString("bsex"));
}
}
}
}
Hmm I'm also trying to get this to work, Have you tried looking at maybe refreshing the LinearLayout every say 5 seconds using a game loop? This may prove to be useful as it helped me with my problem http://aubykhan.wordpress.com/2010/04/25/android-game-programming-the-game-loop/
You may also be able to call onCreate(Bundle) function again while this is a terrible terrible way to do this it will work.