Currently, this is how I implement Chrome custom tabs
String url = "http://www.google.com/";
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(WelcomeFragment.this.getActivity(), Uri.parse(url));
I was wondering, is it possible to have Chrome custom tabs as View object?
The reason I'm asking so that that, previously, I have a fragment, which is having ViewAnimator object. ViewAnimator will in-turn animate between 2 WebViews.
One WevView is displaying mobile version of the web page. Another WebView is displaying desktop version of the web page.
Here's the code which used to alternate between the 2 WebViews
public void updateWebView() {
int index = getCurrentWebViewHolderIndex();
final WebViewHolder webViewHolder = webViewHolders[index];
if (webViewHolder == null) {
return;
}
final WebView webView = webViewHolder.webView;
boolean loadUrl = false;
boolean reload = false;
synchronized (monitor) {
if (false == webViewHolder.loadUrl) {
webViewHolder.loadUrl = true;
loadUrl = true;
} else if (webViewHolder.error) {
webViewHolder.error = false;
reload = true;
}
}
if (loadUrl) {
webView.loadUrl(getUrl(index));
} else if (reload) {
webView.reload();
}
final WebViewFragmentActivity activity = (WebViewFragmentActivity)WebViewFragment.this.getActivity();
if (activity != null) {
final int progress = webViewHolder.progress;
if (progress >= 100) {
activity.setProgressBarVisibilityEx(false);
} else {
activity.setProgressBarVisibilityEx(true);
activity.setProgressEx(progress);
}
}
if (index == 0) {
// Slide from left.
Animation slideInLeftFast = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_in_left_fast);
Animation slideOutRightSlow = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_out_right_slow);
this.webViewViewAnimator.setInAnimation(slideInLeftFast);
this.webViewViewAnimator.setOutAnimation(slideOutRightSlow);
} else {
// Slide from right.
Animation slideInRightFast = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_in_right_fast);
Animation slideOutLeftSlow = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_out_left_slow);
this.webViewViewAnimator.setInAnimation(slideInRightFast);
this.webViewViewAnimator.setOutAnimation(slideOutLeftSlow);
}
if (webViewViewAnimator.getChildCount() >= 2) {
webViewViewAnimator.removeViewAt(0);
}
webViewViewAnimator.addView(webView);
webViewViewAnimator.setDisplayedChild(webViewViewAnimator.getChildCount() - 1);
}
Here's the XML code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/web_view_linear_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal" >
<ViewAnimator
android:id="#+id/web_view_view_animator"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
I was told that Chrome Custom Tabs is having a much better performance than WebView.
However, I don't find a way, to let single fragment holding 2 different Chrome Custom Tabs. As, they are Intent, not View.
But, is there any way, to have Chrome custom tabs as View object?
Chrome Custom Tabs, will launch its own UI on a full screen. You cannot have it like a View in XML or Java/Kotlin.
As per the docs here - https://developer.chrome.com/docs/android/custom-tabs/overview/, it lets you customize 3 things in the UI only -
Toolbar color
Enter and exit animations
Add custom actions to the browser toolbar, overflow menu and bottom toolbar
Related
I have a BottomNavigationView in my android activity, which consists of 4 menuItem. When I tap on the downloads menu I check if there are any downloaded contents available, if available I will allow the navigation to happen and if there is no downloaded content I will show a Toast stating the same and I want the previous tab to remain selected. In iOS I can use the delegate method shouldSelectViewController to determine whether navigation can be allowed or not.
The method signature is specified below:
- (BOOL)tabBarController:(UITabBarController *)tabBarController
shouldSelectViewController:(UIViewController *)viewController;
I tried reselecting the previously selected tab, as a result, the previous item is retained but the selected item color is still assigned to the downloads tab.
private void BottomNavigationItemSelected(object obj, BottomNavigationView.NavigationItemSelectedEventArgs args)
{
Android.Support.V4.App.Fragment fragment = null;
Android.Support.V4.App.Fragment currentFragment = SupportFragmentManager.FindFragmentById(Resource.Id.content_frame);
string title = "";
if (args.Item.ItemId == Resource.Id.menu_explore)
{
_selectedToolbarId = args.Item.ItemId;
title = Resources.GetString(Resource.String.shelf_title);
fragment = _exploreFragment;
_fragmentTag = "Home";
}
else
{
title = args.Item.TitleFormatted.ToString();
}
if (args.Item.ItemId == Resource.Id.menu_dashboard)
{
//COULD BE MADE CONFIGURABLE
//fragment = _dashboardFragment;
_selectedToolbarId = args.Item.ItemId;
fragment = _redesignDashboard;
_fragmentTag = "Dashboard";
}
else if (args.Item.ItemId == Resource.Id.menu_more)
{
_selectedToolbarId = args.Item.ItemId;
fragment = _moreFragment;
_fragmentTag = "More";
}
else if (args.Item.ItemId == Resource.Id.menu_report)
{
_selectedToolbarId = args.Item.ItemId;
fragment = _reportFragment;
_fragmentTag = "Report";
}
else if (args.Item.ItemId == Resource.Id.menu_downloads)
{
List<Product> _downloadProducts = DBService.GetDB().GetDownloadedProducts();
if (_downloadProducts == null || _downloadProducts.Count == 0)
{
_bottomNavigationView.SelectedItemId = _selectedToolbarId;
Toast.MakeText(this, "No downloaded products", ToastLength.Short).Show();
args.Item.SetChecked(false);
}
else
{
_downloadGalleryFragment = new DownloadGalleryFragment(_downloadProducts);
fragment = _downloadGalleryFragment;
_fragmentTag = "Downloads";
}
}
if (fragment != null)
{
_toolbarTitle.Text = title;
ToggleTitle(true);
SupportFragmentManager.BeginTransaction().SetCustomAnimations(Resource.Animation.fab_slide_in_from_right, Resource.Animation.fab_slide_out_to_left).Replace(Resource.Id.content_frame, fragment, _fragmentTag).Commit();
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menu_explore"
android:enabled="true"
android:title="#string/explore"
android:icon="#drawable/explore_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_dashboard"
android:enabled="true"
android:title="#string/dashboard"
android:icon="#drawable/Dashboard_new_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_report"
android:enabled="true"
android:title="#string/reports"
android:icon="#drawable/dashboard_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_downloads"
android:enabled="true"
android:title="#string/menu_downloads"
android:icon="#drawable/download_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_more"
android:enabled="true"
android:title="#string/more_bottombar"
android:icon="#drawable/more_icon"
app:showAsAction="always" />
</menu>
There is no such delegate that works as shouldSelectViewController but what you can do is get the menu items of the bottom nav bar and disables these menu items:
Something like this:
var listMenuItems = new List<IMenuItem>();
for (int i = 0; i < bottomNav.Menu.Size(); i++)
{
listMenuItems.Add(bottomNav.Menu.GetItem(i));
}
Once you have them here you can manipulate them as you like, to enable or disable an item just use the SetEnabled method that takes a boolean as a parameter.
Seems like this is an issue with Android or the Bottom Navigation View. When I executed the reselection of the previous fragment after a small delay of 50milliseconds its working fine. ie the reselected fragment or the previous fragments icon gets highlighted as required.
if (args.Item.ItemId == Resource.Id.menu_downloads)
{
List<Product> _downloadProducts = DBService.GetDB().GetDownloadedProducts();
if (_downloadProducts == null || _downloadProducts.Count == 0)
{
_readProgressTimerTask = new Timer
{
Enabled = true,
Interval = 50,
AutoReset = false
};
_readProgressTimerTask.Elapsed += OnProgressCheckTimeElapsed;
Toast.MakeText(this, this.Resources.GetString(Resource.String.no_downloads), ToastLength.Short).Show();
}
else
{
_downloadGalleryFragment = new DownloadGalleryFragment(_downloadProducts);
fragment = _downloadGalleryFragment;
_fragmentTag = "Downloads";
}
}
private void OnProgressCheckTimeElapsed(System.Object source, ElapsedEventArgs args)
{
this.RunOnUiThread(() =>
{
_bottomNavigationView.SelectedItemId = _selectedToolbarId;
});
}
I recommend that you first read the guideline available here to implement bottom navigation for android.
That what you want doesn't make much sense, an empty screen should be shown saying something like "There is not content available" or the message you like.
In this link BottomNavigationView you can find all public methods and listeners you can set to bottom navigation. As for your case, you might be interested in adding these two.
setOnNavigationItemReselectedListener()
setOnNavigationItemSelectedListener()
Best regards, Pedro.
On some devices such as Samsung S8, navigation bar can be hide or show, that's a question in some condition.
Samsung S8's navigation bar can be hide or show by click left bottom button
I didn't find straight way to determine even if in the Android sources code.
And I google some issues, such as A good solution to check for navigation bar , but it doesn't help.
Any help is very appreciated.
First, credit the original author: https://www.jianshu.com/p/ddfbabd614b6
For the Samsung phones (i.e, S8, S9, etc) you can detect if the navigation bar is showing via listening to a Samsung event.
private static final String SAMSUNG_NAVIGATION_EVENT = "navigationbar_hide_bar_enabled";
Then just listen to this event, and do your thing:
private void checkNavigationBar() {
if (isSamsungVersionNougat()) { // check if Samsung phones
// detect navigation bar
try {
// there are navigation bar
if (Settings.Global.getInt(activity.getContentResolver(), SAMSUNG_NAVIGATION_EVENT) == 0) {
// Your code
// example: galleryViewModel.navigationBarHeight.set(getNavigationBarHeight());
} else { // there are no navigation bar
// Your code
// example: galleryViewModel.navigationBarHeight.set(0);
}
} catch (Exception ignored) {
}
barHideEnableObserver = new BarHideEnableObserver(new Handler());
activity.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SAMSUNG_NAVIGATION_EVENT),
true, barHideEnableObserver);
} else {
galleryViewModel.navigationBarHeight.set(getNavigationBarHeight());
}
}
Use this method, worked for me. Make sure the view has been rendered first to make sure that getHeight() doesn't return 0. Also make sure that the rootView you are using is meant to take up the whole screen.
public static boolean hasNavBar (Activity activity, View rootView) {
if (activity == null || rootView == null)
return true;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
return true;
Display d = activity.getWindowManager().getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
d.getRealMetrics(realDisplayMetrics);
int viewHeight = rootView.getHeight();
if (viewHeight == 0)
return true;
int realHeight = realDisplayMetrics.heightPixels;
return realHeight != viewHeight;
}
I have two tabs in my mainscreen ,one tab displays a map,When i click on the map pin , it opens an information window , i want to click on this window and load a webview with some url content Without hiding the tabbar present on top of screen .
i am able to do this when i load webview in new activity , but it hides the tabbar at the top (material design for forms).
in iOS this was simple i did like below from custom render class
void OnCalloutAccessoryControlTapped (object sender, MKMapViewAccessoryTappedEventArgs e) {
var temp = (uiview)this
temp.addSubView(mywebview)
}
the same thing i want to achieve from android
and i have tried something like
void OnInfoWindowClick (object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin (e.Marker);
if (customPin == null) {
throw new Exception ("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace (customPin.Url)) {
var url = Android.Net.Uri.Parse (customPin.Url+"/app/auswahl-speisen.php");
// var webActivity = new Intent (this.Context, typeof(WebViewFragment));
// webActivity.PutExtra ("Shop Url",url.ToString() );
var inflater = Android.App.Application.Context.GetSystemService (Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null) {
Android.Views.View view;
view = inflater.Inflate (Resource.Layout.WebViewLayout, null);
myWebView = view.FindViewById<Android.Webkit.WebView> (Resource.Id.webview);
myWebView.Settings.JavaScriptEnabled = true;
myWebView.Settings.LoadWithOverviewMode =false;
myWebView.Settings.UseWideViewPort = false;
myWebView.Settings.BuiltInZoomControls = false;
myWebView.Settings.AllowFileAccess = true;
myWebView.Settings.DomStorageEnabled = true;
myWebView.Settings.JavaScriptCanOpenWindowsAutomatically = true;
myWebView.LoadUrl (url.ToString());
myWebView.SetWebViewClient(new MyWebClient ());
//trail first
var me = this.ViewGroup;
me.AddView (myWebView);
//trial second
//this.AddView(myWebview)
}
}
}
In android this code doesn't do anything , it simply complies does nothing.
so i want to know , how to replace the mapview with webview or add webview on top of it .
I am quiet new to android , i am assuming that i can get away with android.views.view not to mess up with fragments
What I would like to achieve is a bit like this
Where "some view" is some custom content (either a page or content view) that is fixed and never changes (like a status bar) and the page would change by normal navigation (through the NavigationPage).
I already have something similar working but "some view" gets re-created every time the user navigates because every page derives from a base page that contains "some view".
public sealed class RootPage : ExtendedMasterDetailPage
{
public RootPage()
{
// 30%
DrawerWidth = .3f;
Master = new MenuPage();
Detail = new NavigationPage(new HomePage());
}
}
public abstract class MasterPage : ContentPage
{
private readonly Grid _root;
private View _content;
// Subclasses set this property to add their content
public View Detail
{
get { return _content; }
set
{
if (_content != null)
_root.Children.Remove(_content);
_content = value;
if (_content != null)
_root.Children.Add(_content, left: 0, top: 1);
OnPropertyChanged();
}
}
protected MasterPage()
{
_root = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
RowDefinitions =
{
new RowDefinition {Height = new GridLength(60, GridUnitType.Absolute)},
new RowDefinition {Height = new GridLength(1, GridUnitType.Star)},
},
RowSpacing = 0
};
// BuildStatusBar() creates the "static" content that is placed above the page
_root.Children.Add(BuildStatusBar(), left: 0, top: 0);
Content = _root;
}
}
public sealed class HomePage : MasterPage
{
public HomePage()
{
// Build content
Detail = ...
}
}
On this blog post the author does something similar to me but in XAML, and he seems fine with his content being reloaded every time; whereas I want that only the content page changes and the header "some view" stays there.
I believe I would need some custom renderer, and the best documentation on that seems to be this swipe to refresh custom renderer. However I cannot figure out how to apply it to my scenario.
I dont think you can that achieve in Xamarin.Forms.
You can change the contents of the content view inside it with animation, but that wont be a right thing to do.
It wont be possible (or too much efforts) to go back through hardware keys.
For specific customer requirement, I need to allow the user of my app ( won't be published in Market) to click on the ActionBar title to execute some actions.
I have been looking in the Android source, but I am not able to find an ID for the actionBar TextView title.
Is there a proper way to handle such clicks?
The title is non-clickable AFAIK. The icon/logo is clickable -- you'll get that via android.R.id.home in onOptionsItemSelected(). Conceivably, the title also routes this way, though they don't mention it and I wouldn't rely upon it.
It sounds like you want a Spinner for the user to choose the actions to execute. If so, use setListNavigationCallbacks(). If you want to remove the title as now being superfluous, use setDisplayOptions(0, DISPLAY_SHOW_TITLE).
If you want something other than a Spinner on the left side of the action bar, use setDisplayOptions(DISPLAY_SHOW_CUSTOM, DISPLAY_SHOW_CUSTOM) and setCustomView(). Note that this approach is not recommended ("Avoid using custom navigation modes in the action bar"), as it may not work well with phones, particularly in portrait mode.
Another possibility would be to remove the title and use a logo instead of the icon, and in the logo have your "title" as part of the image. The whole logo should be clickable, picked up via onOptionsItemSelected().
//onCreate
ActionBar actionBar = getActionBar();
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
// View actionBarView = getLayoutInflater().inflate(R.layout.action_bar_custom_view, null);
actionBar.setCustomView(actionBarView);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
//your logic for click listner
setListenerForActionBarCustomView(actionBarView);
private void setListenerForActionBarCustomView(View actionBarView) {
ActionBarCustomViewOnClickListener actionBarCustomViewOnClickListener = new ActionBarCustomViewOnClickListener();
actionBarView.findViewById(R.id.text_view_title).setOnClickListener(actionBarCustomViewOnClickListener);
}
private class ActionBarCustomViewOnClickListener implements OnClickListener {
public void onClick(View v) {
switch(v.getId()) {
case R.id.text_view_title:
//finish();
break;
}
}
You can set up a custom toolbar from Support Library by declaring <android.support.v7.widget.Toolbar> in your layout (see Chris Banes' answer for full toolbar layout example).
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- We use a Toolbar so that our drawer can be displayed
in front of the action bar -->
<android.support.v7.widget.Toolbar
android:id="#+id/my_awesome_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/main_toolbar"
android:minHeight="?attr/actionBarSize" />
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
After you can add on click listener in your activity just like to most other Views.
Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
setSupportActionBar(toolbar);
toolbar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MyActivity.this, "Test", Toast.LENGTH_LONG).show();
}
});
If you want to capture touch events on title:
toolbar.setOnTouchListener(new View.OnTouchListener() {
Rect hitrect = new Rect();
public boolean onTouch(View v, MotionEvent event) {
if (MotionEvent.ACTION_DOWN == event.getAction()) {
boolean hit = false;
for (int i = toolbar.getChildCount() - 1; i != -1; i--) {
View view = toolbar.getChildAt(i);
if (view instanceof TextView) {
view.getHitRect(hitrect);
if (hitrect.contains((int)event.getX(), (int)event.getY())) {
hit = true;
break;
}
}
}
if (hit) {
//Hit action
}
}
return false;
}
});