Custom component inflates in App but not in Widget - android-widget

Background
I created a custom component to draw on top of an image. At this stage I am merely extending the view.
My primary intention was to create a Widget but could not get it to work. I tried to load the same component in a main Activity, it worked Face Palm.
To be clear I read many many many many ... many post on this topic. This is not a constructor issue.
Symptom
When I load the component in a Widget, I get the following InflateException (no stacktrace).
2020-06-20 09:23:56.368 2602-2602/? W/AppWidgetHostView: Error inflating AppWidget AppWidgetProviderInfo(UserHandle{0}/ComponentInfo{com.package/com.package.WidgetProvider}): android.view.InflateException: Binary XML file line #1: Binary XML file line #1: Error inflating class com.package.MyView
Code
The culprit class
#RemoteViews.RemoteView
public class MyView extends ImageView {
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// Eventually we'll do something mystical here
}
}
Here is the Widget Layout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.package.MyView
android:id="#+id/myview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/myimage"
/>
</LinearLayout>
Note: if I switch com.package.MyView for ImageView, it works. See below (ImageView vs MyView)
Question
How do I get this component to inflate correctly in the Widget?

Unfortunately android does not support any descendants of the supported widget classes (ImageView included)
see note under "Creating the App Widget Layout" in the Build an App Widget documentation page
Although , I did find that even though TextClock is not listed under the supported layouts it is indeed supported on all the devices I tried on.
so maybe don't take that as an absolute fact.

Thanks #Joe for pointing me away from the wrong direction.
What worked
I basically had to customize the existing AnalogClock as follows
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<AnalogClock
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dial="#drawable/mydial"
android:hand_hour="#drawable/myhour"
android:hand_minute="#drawable/myminute"
/>
</LinearLayout>
This is bare bones but it does the job.
For the record
AnalogClock is deprecated
Extending the class (cf #Joe) does not work
Copy pasting AnalogClock source code does not work.
I recommend reading this about building your own AnalogClock widget
Outcome

Related

Do not allow multiple custom formats Tinymce

When playing around in the online editor at this url: https://www.tiny.cloud/docs/demo/format-custom/ it is quite possible to apply multiple custom formats.
I would claim multiple custom formats is the default behaviour, however we have a request to change this. Is it even possible to configure Tinymce to allow only one custom format at a time? Given the screenshot example, let's say it's not allowed to combine "red header" with "bold text"?
This isn't really Episerver-specific, but rather about TinyMCE.
There are examples of people creating a custom format button which only allows one (1) format to be selected: https://codepen.io/thibbiuf/pen/JKBkXy?editors=1000
You could create your own TinyMCE plugin and then add it to the editor in Episerver:
[ModuleDependency(typeof(TinyMceInitialization))]
public class ExtendedTinyMceInitialization : IConfigurableModule
{
public void Initialize(InitializationEngine context)
{
}
public void Uninitialize(InitializationEngine context)
{
}
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Services.Configure<TinyMceConfiguration>(config =>
{
config.Default()
.AddPlugin("custom_styleselect")
.AppendToolbar("custom_styleselect");
});
}
}
There are multiple ways to add a TinyMCE plugin, but one way is to load a custom script file when TinyMCE loads in Episerver by adding something like the following to module.config:
<?xml version="1.0" encoding="utf-8"?>
<module name="Your.Website" >
<clientResources>
<add name="epi-addon-tinymce.main" path="ClientResources/tinymce/custom_styleselect/Plugin.js" resourceType="Script" />
</clientResources>
</module>

Why there are so many way to set CSS for Widgets in GWT or GWTP, I am confused? Can someone clarify this?

Ok, to set a Css for a widget in Gwt or Gwtp, we can do following:
-1. directly from gwt code. Ex: label1.getElement().getStyle().setBackground("blue");
-2. include "ui:style" in UiBinder xml file, but this css only visible to that UiBinder
-3. include "ui:width" in UiBinder xml file, it will visible to all UiBinder
- and there r many way to set the Css directly to the widget in UiBinder.
The one that made me confused is that if i used , ex
<ui:with field='res' type="com.myproj.client.MyResource" />
& if myResource.css has .gwt-TabLayoutPanel then i don't need to use "addStyleNames", ex <g:TabLayputPanel />, it can recognized CSS perfectly.
However, if i add .gwt-ScrollPanel into myResource.css & use <g: ScrollPanel /> then nothing happened.
So I have to create public interface MyCssResource extends CssResource, then add String gwt-ScrollPanel(); to MyCssResource. But Java eclipse do not allow hyphen - in the method name, so I have to change to String gwtScrollPanel();.
Finally, i have to add addStyleNames into <g: ScrollPanel />, ex <g: ScrollPanel addStyleNames="{res.css.gwtScrollPanel}" /> then it will work.
That also means if i want to use .gwt-TabLayoutPanel in MyCssResource, then i need to remove the hyphen - & this will cause inconsistency in my code.
So, can someone explain to me what is going on here? I am confused?
The only think you should be aware about, is that GWT obfuscates class names when you use then in ui-binders. For instance:
<ui:style>
.gwtTabLayoutPanel {}
</ui:style>
<g:TabLayoutPanel addStyleNames="{style.gwtTabLayoutPanel}" />
.gwtTabLayoutPanel will be renamed to something like .AB in the final injected style-sheet.
But most GWT widgets, are styled with un-obfuscated class names, so for using them in ui-binders files, you have to define as external in order to prevent GWT compier to obfuscate the class name:
<ui:style>
#external .gwt-TabLayoutPanel;
.gwt-TabLayoutPanel {}
</ui:style>
<g:TabLayoutPanel />
That's because when you create a TabLayoutPanel, it has a default class called .gwt-TabLayoutPanel. So you don't need to add that class manually in to your TabLayoutPanel.Just create a TabLayoutPanel and you will see the class ".gwt-TabLayoutPanel" is already there.
But ScrollPanel doesn't comes with a default class called .gwt-ScrollPanel. It is just a div. Try creating a ScrollPanel and see. It doesn't have any classes added initially.see the screenshot
If you want to add a class called .gwt-ScrollPanel you will have to do it manually.

Custom tags in UiBinder files

When using a <g:LayoutPanel> in UiBinder.ui.xml files, you can specify <g:layer> tags. Some other Google-built widgets have special tags like that as well - <g:tab> even has a sub-tag, <g:header>.
How can I specify these for my own widgets?
The new answer to this question, after some GWT improvements, is at https://stackoverflow.com/a/11785903/439317 . Copied below to avoid moderator deletion (maybe?).
You can use #UiChild to declare special functions in your widgets accessible in UiBinders.
for example,
class MyPanel extends AbsolutePanel {
#UiChild
public void addAt(Widget w, String parameter1, String parameter2) {
....
Then, in your uiBinder, you can say
<custom:MyPanel>
<custom:at parameter1="HI" parameter2="Anything you like!">
<g:AnySingleWidget />
</custom:at>
</custom:MyPanel>
See #UiChild at http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/uibinder/client/UiChild.html
What you're looking for is a custom element parser for UiBinder. See this issue. Unfortunately it's not supported yet.
You might be interested in this post for some guidance on how to extend the current parser on your own.

GWT CssResource Customization

I'm writing a GWT widget using UIBinder and MVP. The widget's default styles are defined in TheWidgetView.ui.xml:
<ui:style type="com.widgetlib.spinner.display.TheWidgetView.MyStyle">
.textbox {
border: 1px solid #red;
}
.important {
font-weight: bold;
}
</ui:style>
The widget's CssResource interface is defined in TheWidgetView.java:
public class TheWidgetView extends HorizontalPanel implements TheWidgetPresenter.Display {
// ... some code
#UiField MyStyle style;
public interface MyStyle extends CssResource {
String textbox();
String important();
}
// ... more code
}
I'd like the consumer of this widget to be able to customize part of the widget's styles and to have this in their MyExample.ui.xml:
<ui:style type="com.consumer.MyExample.MyStyle">
.textbox {
border: 2px solid #black;
}
</ui:style>
And this be their MyExample.java:
public class MyExample extends Composite {
// ... some code
#UiField MyStyle style;
interface MyStyle extends TheWidgetView.MyStyle{
String textbox();
}
// ... more code
}
Is there a way that my widget can have default styles, but that the consumer of the widget can override one of them? When an interface extends TheWidgetView.MyStyle, the of the widget consumer needs to define all the styles listed in that parent interface. I've seen some widget libraries have the widget's constructor take in a ClientBundle as parameter, which I suppose could apply to CssResource. Although, I'm not sure how I'd pass in this style object in a constructor invoked by UIBinder.
Thanks much in advance!
I have playing around with something similar to make my application skinable. I would start by looking at the source code for any of the cell widgets. They seem to take the resources as a constructor, however they also have constructors that use GWT.create(Resources.class) to create the resources if they are not provided. As far as your question about using this template for UIBinder, there are 3 options mentioned here. However you may run into chicken and the egg issues when trying to define the style inside the UIBinder.
The issue you are running into with your other code is that your 2 different implementations of the style because uibinder creates it's own resource bundle which doesn't reference the parent's css. There are 3 solutions:
1) Tear the css out of the ui binder file into it's own file and use ui:with combined with either a provided field or uifactory to inject the style using your own resource bindle where you can compound the sources (i.e. #Source({DEFAULT_CSS, "myCss.css"})).
2) Your other option is to look at the generated code and use the syntax they are using to refernce the css within the uibinder file, however there are 2 issues you will have to overcome: the chicken and the egg problem and the fact that google can change this without telling you and break your stuff. Here is an example of the generated client bundle from one of my files:
#Source("uibinder:com.foo.gwt.client.workspace.select.SelectWorkspaceViewImpl_SelectWorkspaceViewImplUiBinderImpl_GenCss_style.css")
SelectWorkspaceViewImpl_SelectWorkspaceViewImplUiBinderImpl_GenCss_style style();
3) The last solution is to use deferred binding to replace the default style.
<replace-with class="com.foo.gwt.laf.mpe.resource.CellTableResources">
<when-type-is class="com.google.gwt.user.cellview.client.CellTable.Resources" />
<when-property-is name="laf" value="MPE" />
</replace-with>

GWT - How to define a Widget outside layout hierarchy in uibinder xml file

this is my first post. I hope someone could help me.
I'm looking for a way to define a widget in UiBinder XML layout file separately, without being part of the layout hierachy.
Here's a small example:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:Label ui:field="testlabel" text="Hallo" />
<g:HTMLPanel>
...
</g:HTMLPanel>
The compile fails since the ui:UiBinder element expects only one child element.
In Java Code i will access and bind the Label widget as usual:
#UiField Label testlabel;
For example, this could be useful when you define a Grid or FlexTable - i want to define the Labels for the table header within the XML layout file, not programmatically within the code.
Many thanks in advance
Sorry, no can do, UiBinder just doesn't support this sort of syntax. Think about it - where would this Widget go? Should it be added to the DOM tree? It also makes it hard for the compiler to tell if this code should be pruned as unused.
You'll have to wait for the GWT team to create custom tags for Grid (like they did with, for example, DockLayoutPanel). I wouldn't expect such functionality for the FlexTable though, since it's meant by design for dynamic content, meaning adding it programmatically anyway.
Understanding the UiBinder Dev Guide is the key to figuring out how to structure your UiBinder templates cleanly.
So, if all you want to do is create a widget and not have it initially in the DOM, then don't mention it at all in the UiBinder template. Instead, in the Java file that goes with your UiBinder template, create it and add it to a panel that's defined in your template.
For instance, put this in your template:
<g:HTMLPanel ui:field='container'>
...
And put this in your Java file:
#UiField HTMLPanel container;
public MyWidget() {
initWidget(uiBinder.createAndBindUi(this));
Label testLabel = new Label("hallo");
// Do whatever you want to testLabel
container.add(testLabel);
}
I am not certain if you and have the same unusual motivation to place more than two root widgets under the uibinder tag. Anyway, this is how I do it.
Since uibinder tag allows only one root widget, I place an HTML ui tag as the root widget, and then pile up my multiple pseudo-root widgets within that tag.
In the following example, notice that the actual root widget does not have ui:field name, because we don't intend to use it. As far as we are concerned, the effective root widgets are "tabLayout" and "content".
File Hello.ui.xml:
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTML>
<g:TabLayoutPanel ui:field="tabLayout" ... >
blah ... blah
</g:TabLayoutPanel>
<g:VerticalPanel ui:field="content">
blah .. blah
</g:VerticalPanel>
</g:HTML>
</ui:UiBinder>
Do not extend composite as your java code behind the template.
Create a class imported by your module entrypoint or use the template directly on your module.
I have various reasons why I need more than one root widget. In this example, I attach the "effective root widget" conditionally:
public class Hello
implements EntryPoint{
private static HelloUiBinder uiBinder = GWT.create(HelloUiBinder.class);
interface HelloUiBinder
extends UiBinder<Widget, Hello>{}
#UiField TabLayoutPanel tabLayout;
#UiField VerticalPanel content;
#Override public void onModuleLoad() {
uiBinder.createAndBindUi(this);
if (condition1)
RootPanel.get().add(tabLayout);
else
RootPanel.get().add(content);
blah ... blah
}
}
So, the trick is not to use the actual root widget at all.