I'm trying to get a button to call on a function. The only problem is, the function calls a parameter from another function and I'm getting an error: SetRecipe (int) in AddRecipe cannot be applied to (). I have no idea what I'm meant to pass/call whatever the correct terminology is.
The button code is:
Button saveRecipe = (Button) findViewById(R.id.btnAddRecipe);
saveRecipe.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SetRecipe();
}
});
And the functions are:
public int getDifficulty (View v)
{
int Difficulty = -1;
boolean checked = ((RadioButton) v).isChecked();
switch(v.getId()) {
case R.id.btnDiff1:
if (checked)
Difficulty = 1;
break;
case R.id.btnDiff2:
if (checked)
Difficulty = 2;
break;
case R.id.btnDiff3:
if (checked)
Difficulty = 3;
break;
case R.id.btnDiff4:
if (checked)
Difficulty = 4;
break;
case R.id.btnDiff5:
if (checked)
Difficulty = 5;
break;
}
return Difficulty;
}
public void SetRecipe (int Difficulty)
{
TextView RecipeNameView = (TextView) findViewById(R.id.editRecipeName);
TextView CookTimeView = (TextView) findViewById(R.id.editCookTime);
Spinner MealTypeView = (Spinner) findViewById(R.id.editMeal);
TextView HowToView = (TextView) findViewById(R.id.editHowTo);
String RecipeName = RecipeNameView.getText().toString();
int CookTime = Integer.parseInt(CookTimeView.getText().toString());
String MealType = MealTypeView.getSelectedItem().toString();
String HowTo = HowToView.getText().toString();
DatabaseHandler dbh = new DatabaseHandler(this);
dbh.SetRecipe(RecipeName, CookTime, Difficulty, MealType, HowTo);
}
All I'm looking to do is get the int Difficulty from the getDifficulty function and pass it to the SetRecipe function. The SetRecipe function will then be called on when the user hits a button.
If it's a silly mistake then I do apologise, I am extremely new to coding and so I don't really know what I'm doing. Any help would be greatly appreciated!
Try adding the android:onClick="getDifficulty" attribute to the btnAddRecipe tag and remove the onClickListener...
This should work
Instead of saying Difficulty = 1, 2, 3,4 Can you call it directly?
SetRecipe(1);
Also...when I implement View.OnClickListener...
public class yourclass extends AppCompatActivity implements View.OnClickListener {
Button setrecipe1;
Button setrecipe2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.yourlayout);
setrecipe1= (Button) findViewById(R.id.setrecipe1);
setrecipe2= (Button) findViewById(R.id.setrecipe2);
setrecipe1.setOnClickListener(this);
setrecipe2.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.setrecipe1:
SetRecipe(1);
break;
case R.id.setrecipe2:
SetRecipe(2);
break;
default:
break;
}
}
}
before your OnCreate method declare:
int Difficulty;
in your getDifficulty method remove the int:
int Difficulty = -1; ==> Difficulty = -1;
then inside your click button :
Button saveRecipe = (Button) findViewById(R.id.btnAddRecipe);
saveRecipe.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SetRecipe(Difficulty);
}
});
Good luck
Related
I am writing a modal component for blazor but i am struggeling to find a solution which fulfills the requirement to do both at the same time:
close ONLY when clicked outside modal content
update C# object state (Visibility flag) accordingly
scenario 1
If i use the C# based approach with #onclick="WrapperClicked" i can update the Visibility state very easy, but struggle to get the DOM click event and therefore cannot distinguish between a wrapper and a wrapper content click.
The c# MouseEventArgs do not contain properties to distinguish the clicked dom element.
scenario 2
Is based around uncommenting the code for
private Dictionary<string, object> ComponentValues()
{
var values = new Dictionary<string, object>();
// if(CloseOnModalFrameClickInternal)
// values.Add("onclick", "Amusoft.Components.ModalDialog.closeEvent(this, event);");
return values;
}
With this version it is simple to get access to the dom click event - but i cannot pass a DotNetObjectReference to that handler, to be able to call back into c# and update my components state.
Question
Does anyone have ideas how to resolve this deep interop scenario?
typescript code to distinguish wrapper click from wrapper content click:
public static closeEvent(dotNetHelper: any, event: MouseEvent): void {
console.log(event);
console.log(dotNetHelper);
let target = event.target as HTMLElement;
if(target != null && target?.classList?.contains("amu-modal-wrapper")){
// element.style.setProperty("display", "none");
console.log("breakpoint landed");
// valueOfReference.invokeMethodAsync("JsSetVisibility", false);
}
}
Additional Scss code compared to default code:
.amu-modal-wrapper {
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 20px;
.amu-modal-content {
width: 100%;
height: 100%;
background-color: orange;
}
}
Modal component code:
#using Microsoft.JSInterop
#implements IDisposable
<div #ref="wrapper" class="amu-modal-wrapper" #attributes="ComponentValues()" #onclick="WrapperClicked" style="padding: #(Padding)px; display: #(VisibleInternal ? "block" : "none")">
<div class="amu-modal-content">
<h3>#Headline</h3>
#ChildContent
</div>
</div>
#code {
private ElementReference wrapper;
private DotNetObjectReference<ModalDialog> _self;
[Parameter]
public string Headline
{
get => HeadlineInternal;
set => HeadlineInternal = value;
}
[Parameter]
public EventCallback<string> HeadlineChanged { get; set; }
private string _headlineInternal;
private string HeadlineInternal
{
get { return _headlineInternal; }
set
{
if (EqualityComparer<string>.Default.Equals(_headlineInternal, value))
return;
_headlineInternal = value;
HeadlineChanged.InvokeAsync(value);
}
}
[Parameter]
public RenderFragment ChildContent { get; set; }
[Inject]
public IJSRuntime JsRuntime { get; set; }
[Parameter]
public int Padding { get; set; } = 100;
[Parameter]
public bool Visible
{
get => VisibleInternal;
set => VisibleInternal = value;
}
[Parameter]
public EventCallback<bool> VisibleChanged { get; set; }
private bool _visibleInternal;
private bool VisibleInternal
{
get { return _visibleInternal; }
set
{
if (EqualityComparer<bool>.Default.Equals(_visibleInternal, value))
return;
_visibleInternal = value;
VisibleChanged.InvokeAsync(value);
}
}
[Parameter]
public bool CloseOnModalFrameClick
{
get => CloseOnModalFrameClickInternal;
set => CloseOnModalFrameClickInternal = value;
}
private Dictionary<string, object> ComponentValues()
{
var values = new Dictionary<string, object>();
// if(CloseOnModalFrameClickInternal)
// values.Add("onclick", "Amusoft.Components.ModalDialog.closeEvent(this, event);");
return values;
}
private bool CloseOnModalFrameClickInternal { get; set; } = true;
public void Hide()
{
VisibleInternal = false;
StateHasChanged();
}
public void Show()
{
VisibleInternal = true;
StateHasChanged();
}
protected override void OnInitialized()
{
_self = DotNetObjectReference.Create(this);
base.OnInitialized();
}
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
JsRuntime.InvokeVoidAsync("Amusoft.Components.ModalDialog.initialize", wrapper);
}
public void Dispose()
{
_self?.Dispose();
}
private Task WrapperClicked(MouseEventArgs arg)
{
JsRuntime.InvokeVoidAsync("Amusoft.Components.ModalDialog.closeEvent", _self, arg);
return Task.CompletedTask;
}
}
You dont need Java.
<div class="modal-outer" #onclick="OnBackgroundClicked">
<div class="modal-inner" #onclick:stopPropagation="true">
From my repo
It is now a free nuget package as of about 24hrs ago
I am loading images from Wikipedia into a Grid view. For the most part this is working correctly. Because there could possible be up to 200 or more images being loaded I am try to run it in a new thread. I see a definite delay when scrolling from my Album tab to the Artist tab that is loading the images. I am also see some lag as images are still getting load while scrolling up and down the list. Also when I scroll back to the top of the list place holders that previously occupied by the default image because I am unable to get an image from Wikipedia are now occupied by images from another artist.
When I scroll back to the song list and then back to the artist list the view is reset but it still has a lot of delay when going into the artist tab.
This image is what the screen looks like when first entering the Artist tab.
This image is what the screen looks like after scrolling to the bottom of the list and back to the top.
As you can see the <unknow. and AJR have had their default image replaced.
Here is my code that I am calling to load the images from Wikipedia.
#Override
public void onBindViewHolder(#NonNull ARV holder, int position) {
Artist artist = artistList.get(position);
if(artist!=null) {
holder.artistName.setText(artist.artistName);
String bandName = artist.artistName;
bandName = bandName.replace(' ','_');
try {
String imageUrl = cutImg(getUrlSource("https://en.wikipedia.org/w/api.php?action=query&titles="+bandName+"&prop=pageimages&format=json&pithumbsize=250"));
URL url = new URL(imageUrl);
ImageLoader.getInstance().displayImage(imageUrl, holder.artistImage,
new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnLoading(R.drawable.album)
.resetViewBeforeLoading(true).build());
} catch (IOException e) {
e.printStackTrace();
}
/*ImageLoader.getInstance().displayImage(getCoverArtPath(context,artist.id),holder.artistImage,
new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnLoading(R.drawable.album)
.resetViewBeforeLoading(false).build());*/
}
}
private StringBuilder getUrlSource(String site) throws IOException {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
URL localUrl = null;
localUrl = new URL(site);
URLConnection conn = localUrl.openConnection();
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line = "";
String html;
StringBuilder ma = new StringBuilder();
while ((line = reader.readLine()) != null) {
ma.append(line);
Log.i(ContentValues.TAG, "StringBuilder " + ma);
}
Log.i(ContentValues.TAG, "Final StringBuilder " + ma);
return ma;
}
public static String cutImg(StringBuilder split){
int start=split.indexOf("\"source\":")+new String("\"source\":\"").length();
split.delete(0, start);
split.delete(split.indexOf("\""), split.length());
Log.i(ContentValues.TAG, "StringBuilder " + split);
return split.toString();
}
Here is the code that is call the Artist Fragment.
public class ArtistFragment extends Fragment {
int spanCount = 3; // 2 columns
int spacing = 20; // 20px
boolean includeEdge = true;
private RecyclerView recyclerView;
private ArtistAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_artist, container, false);
recyclerView = view.findViewById(R.id.artistFragment);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
Thread t = new Thread()
{
public void run()
{
// put whatever code you want to run inside the thread here.
new LoadData().execute("");
}
};
t.start();
return view;
}
public class LoadData extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... strings) {
if(getActivity()!=null) {
adapter=new ArtistAdapter(getActivity(),new ArtistLoader().artistList(getActivity()));
}
return "Executed";
}
#Override
protected void onPostExecute(String s) {
recyclerView.setAdapter(adapter);
if(getActivity()!=null) {
recyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge));
}
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
}
}
I have also tried this using Picasso using the following code:
bandName = artist.artistName;
bandName = bandName.replace(' ','_');
try {
String imageUrl = cutImg(getUrlSource("https://en.wikipedia.org/w/api.php?action=query&titles="+bandName+"&prop=pageimages&format=json&pithumbsize=250"));
URL url = new URL(imageUrl);
Picasso.get().load(imageUrl).placeholder(R.drawable.album)
.error(R.drawable.artistdefault).into(holder.artistImage);
} catch (IOException e) {
e.printStackTrace();
}
The results are pretty much the same as when I used Android-Universal-Image-Loader. I have been try for several days to fix this, I have tried several different examples that I found on Stack overflow but none of them seem to resolve the issues I am seeing. I am hoping that someone will be able to identify what I am doing incorrectly.
Thanks in advance.
ArtistFragmentconverted to Kotlin
class ArtistFragment : Fragment() {
var spanCount = 3 // 2 columns
var spacing = 20 // 20px
var includeEdge = true
var retrofit: Retrofit? = null
var wikiService: WikiService? = null
var adapter: ArtistAdapter? = null
private var recyclerView: RecyclerView? = null
private var viewModelJob = Job()
private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private var progress_view: ProgressBar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_artist, container, false)
recyclerView = view.findViewById(R.id.artistFragment)
recyclerView?.setLayoutManager(GridLayoutManager(activity, 3))
progress_view = view.findViewById(R.id.progress_view)
initWikiService()
initList()
//LoadData().execute("")
return view
}
override fun onDestroy() {
super.onDestroy()
viewModelJob.cancel()
}
private fun initList() {
recyclerView!!.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, includeEdge))
adapter = ArtistAdapter(this)
adapter?.items = ArrayList()
adapter?.listener = this
recyclerView?.adapter = adapter
viewModelScope.launch {
progress_view.visibility = View.VISIBLE
val wikiPages = getWikiPages()
adapter?.items = wikiPages
progress_view?.visibility = View.GONE
}
}
private suspend fun getWikiPages(): ArrayList<Artist> {
val newItems = ArrayList<Artist>()
withContext(Dispatchers.IO) {
ArtistData.artists.map { artist ->
async { wikiService?.getWikiData(artist) }
}.awaitAll().forEach { response ->
val pages = response?.body()?.query?.pages
pages?.let {
for (page in pages) {
val value = page.value
val id = value.pageid?.toLong() ?: value.title.hashCode().toLong()
val title = value.title ?: "Unknown"
val url = value.thumbnail?.source
newItems.add(Artist(id, title, albumCount = 0, songCount = 0, artistUrl = url!!))
}
}
}
}
return newItems
}
private fun initWikiService() {
retrofit = Retrofit.Builder()
.baseUrl("https://en.wikipedia.org/")
.addConverterFactory(GsonConverterFactory.create())
.build()
wikiService = retrofit?.create(WikiService::class.java)
}
I believe I have resolved most of the issues I was previously seeing I am now down to the following problems:
Artist.item.map { artist -> - Not sure how this should be called, Unresolved reference: item
}.awaitAll().forEach { response -> = forEach is telling me Overload resolution ambiguity. All these functions match.
public inline fun Iterable<TypeVariable(T)>.forEach(action: (TypeVariable(T)) → Unit): Unit defined in kotlin.collections
public inline fun <K, V> Map<out TypeVariable(K), TypeVariable(V)>.forEach(action: (Map.Entry<TypeVariable(K), TypeVariable(V)>) → Unit): Unit defined in kotlin.collections
newItems.add(Artist(id, title, url)) - I know that the variables for the Artist Model need to go here, but when I put them there they are unresolved.
I have reworked the ArtistAdapter not sure if it is correct though.
class ArtistAdapter(private val context: ArtistFragment, private val artistList: List<Artist>?) : RecyclerView.Adapter<ArtistAdapter.ARV>() {
private var dimension: Int = 64
init {
val density = context.resources.displayMetrics.density
dimension = (density * 64).toInt()
hasStableIds()
}
var items: MutableList<Artist> = ArrayList()
set(value) {
field = value
notifyDataSetChanged()
}
var listener: Listener? = null
interface Listener {
fun onItemClicked(item: Artist)
abstract fun ArtistAdapter(context: ArtistFragment): ArtistAdapter
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ARV {
return ARV(LayoutInflater.from(parent.context).inflate(R.layout.artist_gride_item, parent,
false))
}
override fun onBindViewHolder(holder: ARV, position: Int) {
holder.onBind(getItem(position))
}
private fun getItem(position: Int): Artist = items[position]
override fun getItemId(position: Int): Long = items[position].id
override fun getItemCount(): Int {
return artistList?.size ?: 0
}
inner class ARV(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
private val artistNameView: TextView = itemView.findViewById(R.id.artistName)
private val artistAlbumArtView: SquareCellView = itemView.findViewById(R.id.artistAlbumArt)
fun onBind(item: Artist) {
artistNameView.text=item.artistName
if(item.artistURL!=null) {
Picasso.get()
.load(item.artistURL)
.resize(dimension, dimension)
.centerCrop()
.error(R.drawable.artistdefualt)
.into(artistAlbumArtView)
} else {
artistAlbumArtView.setImageResource(R.drawable.artistdefualt)
}
itemView.setOnClickListener(this)
}
override fun onClick(view: View) {
val artistId = artistList!![bindingAdapterPosition].id
val fragmentManager = (context as AppCompatActivity).supportFragmentManager
val transaction = fragmentManager.beginTransaction()
val fragment: Fragment
transaction.setCustomAnimations(R.anim.layout_fad_in, R.anim.layout_fad_out,
R.anim.layout_fad_in, R.anim.layout_fad_out)
fragment = ArtistDetailsFragment.newInstance(artistId)
transaction.hide(context.supportFragmentManager
.findFragmentById(R.id.main_container)!!)
transaction.add(R.id.main_container, fragment)
transaction.addToBackStack(null).commit()
}
}
}
Logcat Snippet
java.lang.NoSuchMethodError: No static method metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; in class Ljava/lang/invoke/LambdaMetafactory; or it
s super classes (declaration of 'java.lang.invoke.LambdaMetafactory' appears in /apex/com.android.art/javalib/core-oj.jar)
at okhttp3.internal.Util.<clinit>(Util.java:87)
at okhttp3.internal.Util.skipLeadingAsciiWhitespace(Util.java:321)
at okhttp3.HttpUrl$Builder.parse(HttpUrl.java:1313)
at okhttp3.HttpUrl.get(HttpUrl.java:917)
at retrofit2.Retrofit$Builder.baseUrl(Retrofit.java:506)
at com.rvogl.androidaudioplayer.fragments.ArtistFragment.initWikiService(ArtistFragment.kt:103)
at com.rvogl.androidaudioplayer.fragments.ArtistFragment.onCreateView(ArtistFragment.kt:43)
It looks to me as a known bug with Picasso.
Try to load default image manually so it won't be replaced with cached one.
Update 14.10.20:
I think the main problem is that you load network content in adapter in rather ineffective way. I suggest to form a list of all urls at first, leaving only image load in adapter.
Also reccomend you to use rerofit2 for network calls and something for async work instead of AsyncTask: rxJava, courutines, flow etc.
I created a sample project to load data async using retrofit2+coroutines.
In activity:
private val viewModelScope = CoroutineScope(Dispatchers.Main)
private fun initWikiService() {
retrofit = Retrofit.Builder()
.baseUrl("https://en.wikipedia.org/")
.addConverterFactory(GsonConverterFactory.create())
.build()
wikiService = retrofit?.create(WikiService::class.java)
}
private fun initList() {
viewModelScope.launch {
val wikiPages = getWikiPages()
adapter?.items = wikiPages
}
}
private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private suspend fun getWikiPages(): ArrayList<Item> {
val newItems = ArrayList<Item>()
withContext(IO) {
ArtistData.artists.map { artist ->
async { wikiService?.getWikiData(artist) }
}.awaitAll().forEach { response ->
val pages = response?.body()?.query?.pages
pages?.let {
for (page in pages) {
val value = page.value
val id = value.pageid?.toLong() ?: value.title.hashCode().toLong()
val title = value.title ?: "Unknown"
val url = value.thumbnail?.source
newItems.add(Item(id, title, url))
}
}
}
}
return newItems
}
In viewHolder:
fun onBind(item: Item) {
if (item.url != null) {
Picasso.get()
.load(item.url)
.resize(dimension, dimension)
.centerCrop()
.error(R.drawable.ic_baseline_broken_image_24)
.into(pictureView)
} else {
pictureView.setImageResource(R.drawable.ic_baseline_image_24)
}
}
In adapter: add hasStableIds() to constructor and override getItemId method:
init {
hasStableIds()
}
override fun getItemId(position: Int): Long = items[position].id
Retrofit Service:
interface WikiService {
#GET("/w/api.php?action=query&prop=pageimages&format=json&pithumbsize=250")
suspend fun getWikiData(#Query("titles") band: String): Response<WikipediaResponse?>
}
How can I run some Javascript functions in a Blazor app, without using the JS interop or jQuery? Just plain old Javascript functions that interact with the DOM, independently of Blazor.
I added my script right before the closing </body> tag:
<script src="app.js"></script>
And in app.js I have the following:
var elements = document.querySelectorAll(".some-element");
elements.forEach(function (element) {
element.addEventListener("click", (e) => {
alert("Hello");
});
});
Of course the selector finds no element. I'm guessing they aren't yet present in the DOM at that point? How can I run that script without using the JS interop or jQuery?
If your okay with using jquery you can use an on handler, which will apply to dynamically added elements
$("body").on("click", ".some-element", function(){
alert("Hello");
});
You can do natively too, but seems sketchy IMHO:
document.querySelector('body').addEventListener('click', function(event) {
if (event.target.className.toLowerCase() === 'some-element') {
alert("Hello");
}
});
Note you have weird javascripty things to worry about now like z-index if the click event actually gets up to the body, but it should work for simple stuff.
Main question is WHY would you want to do this in Blazor - whole point is you can use C# events instead you crazy person!!
Create a page like this:
<div height="#($"{Height}px")" width="#($"{Width}px")">
Test
</div>
<button #onclick="DoubleSize">Double Size</button>
#code
{
public int Width { get; set; } = 50;
public int Height { get; set; } = 50;
private void DoubleSize()
{
Width = Width * 2;
Height = Height * 2;
}
}
On render the height and width are 50px:
Then you click the Button, and they change to 100px:
Similarly with CSS if that's what you were talking about:
<div style="width: #($"{Width}px"); height: #($"{Height}px")">
Test
</div>
This is interacting with the properties of the DOM elements without using Javascript... you can do this with any property. You don't need to use Javascript - you just bind them to your properties in C#...
You shouldn't need to 'retrieve the final properties of the rendered DOM elements' as you should be controlling them from C# and not worrying about the DOM
I'm pretty new in Blazor, so sorry if it's not the correct way to do it in Balzor. But I think that you need always use JS interop to use JavaScript function. You want to execute some script, but the question is: when do you want to execute the script? I imagine you want to execute an action after navigate to a page, after make a click in a button... and all this events happens in Blazor
If you ask about relationate an blazor element with the doom you need use #ref
A little example. You create a .js like
var auxiliarJs = auxiliarJs || {};
auxiliarJs.getBoundingClientRect = function (elementRef) {
let result = elementRef? elementRef.getBoundingClientRect():
{left: 0,top: 0,right: 0,bottom: 0,x: 0,y: 0,width: 0,height: 0 };
return result;
}
auxiliarJs.executeFunction = function (elementRef, funciones) {
let res = null;
try {
if (Array.isArray(funciones)) {
functiones.forEach(funcion => {
elementRef[funcion]()
});
}
else
res = elementRef[funciones]();
}
catch (e) { }
if (res)
return res;
}
auxiliarJs.setDocumentTitle = function (title) {
document.title = title;
};
And a service.cs with his interface
public interface IDocumentService
{
Task<ClientRect> getBoundingClientRect(ElementReference id);
Task setDocumentTitle(string title);
Task<JsonElement> executeFunction(ElementReference id, string funcion);
Task executeFunction(ElementReference id, string[] funciones);
}
public class DocumentService:IDocumentService
{
private IJSRuntime jsRuntime;
public DocumentService(IJSRuntime jsRuntime)
{
this.jsRuntime = jsRuntime;
}
public Dictionary<string, object> JSonElementToDictionary(JsonElement result)
{
Dictionary<string, object> obj = new Dictionary<string, object>();
JsonProperty[] enumerador = result.EnumerateObject().GetEnumerator().ToArray();
foreach (JsonProperty prop in enumerador)
{
obj.Add(prop.Name, prop.Value);
}
return obj;
}
public async Task<ClientRect> getBoundingClientRect(ElementReference id)
{
return await jsRuntime.InvokeAsync<ClientRect>("auxiliarJs.getBoundingClientRect", id);
}
public async Task setDocumentTitle(string title)
{
await jsRuntime.InvokeVoidAsync("auxiliarJs.setDocumentTitle", title);
}
public async Task<JsonElement> executeFunction(ElementReference id,string funcion)
{
var result= await jsRuntime.InvokeAsync<JsonElement>
("auxiliarJs.executeFunction", id, funcion);
return result;
}
public async Task executeFunction(ElementReference id, string[] funciones)
{
await jsRuntime.InvokeVoidAsync("auxiliarJs.executeFunction", id, funciones);
}
}
public class ClientRect
{
public float left{ get; set; }
public float top { get; set; }
public float right { get; set; }
public float bottom { get; set; }
public float x { get; set; }
public float y { get; set; }
public float width { get; set; }
public float height { get; set; }
}
Well, you inject the service as usual in program.cs
public static async Task Main(string[] args){
....
builder.Services.AddSingleton<IDocumentService, DocumentService>();
}
And in your component razor
#inject IDocumentService document
<div #ref="mydiv"></div>
<input #ref="myinput">
<button #onclick="click">click</button>
#code{
private ElementReference mydiv;
private ElementReference myinput;
click(){
ClientRect rect = await document.getBoundingClientRect(mydiv);
document.setDocumentTitle("New Title");
document.executeFunction(myinput 'focus')
}
}
Blazor "overwrites" all attached JS Events during render process
window.attachHandlers = () => {
var elements = document.querySelectorAll(".some-element");
elements.forEach(function (element) {
element.addEventListener("click", (e) => {
alert("Hello");
});
});
and in razor page
protected override void OnAfterRender(bool firstRender)
{
if(firstRender)
{
JSRuntime.InvokeVoidAsync("attachHandlers");
}
}
I want to use Google's Text-To-Speech API in an Android app but I only could find the way to do it from web (Chrome). This is my first attempt to play "Hello world" from the app.
playTTS is the onClick and it is been executed, but no sound is played. Is there any JS/Java library I need to import? Is it possible to generate an audio file from it?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBrowser = new WebView(this);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
public void playTTS(View view) {
myBrowser.loadUrl("javascript:speechSynthesis.speak(
SpeechSynthesisUtterance('Hello World'))");
}
In Android java code your Activity/other Class should implement TextToSpeech.OnInitListener. You will get a TextToSpeech instance by calling TextToSpeech(context, this). (Where context refers to your application's Context -- can be this in an Activity.) You will then receive a onInit() callback with status which tells whether the TTS engine is available or not.
You can talk by calling tts.speak(textToBeSpoken, TextToSpeech.QUEUE_FLUSH, null) or tts.speak(textToBeSpoken, TextToSpeech.QUEUE_ADD, null). The first one will interrupt any "utterance" that is still being spoken and the latter one will add the new "utterance" to a queue. The last parameter is not mandatory. It could be an "utterance id" defined by you in case you want to monitor the TTS status by setting an UtteranceProgressListener. (Not necessary)
In Java code a simple "TTS talker" class could be something like:
public class MyTtsTalker implements TextToSpeech.OnInitListener {
private TextToSpeech tts;
private boolean ttsOk;
// The constructor will create a TextToSpeech instance.
MyTtsTalker(Context context) {
tts = new TextToSpeech(context, this);
}
#Override
// OnInitListener method to receive the TTS engine status
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
ttsOk = true;
}
else {
ttsOk = false;
}
}
// A method to speak something
#SuppressWarnings("deprecation") // Support older API levels too.
public void speak(String text, Boolean override) {
if (ttsOk) {
if (override) {
tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
}
else {
tts.speak(text, TextToSpeech.QUEUE_ADD, null);
}
}
}
}
Code for TTS:
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.widget.Toast;
import java.util.HashMap;
import java.util.Locale;
public class KiwixTextToSpeech {
public static final String TAG_ASKQ = "askq;
private Context context;
private OnSpeakingListener onSpeakingListener;
private WebView webView;
private TextToSpeech tts;
private boolean initialized = false;
/**
* Constructor.
*
* #param context the context to create TextToSpeech with
* #param webView {#link android.webkit.WebView} to take contents from
* #param onInitSucceedListener listener that receives event when initialization of TTS is done
* (and does not receive if it failed)
* #param onSpeakingListener listener that receives an event when speaking just started or
* ended
*/
public KiwixTextToSpeech(Context context, WebView webView,
final OnInitSucceedListener onInitSucceedListener,
final OnSpeakingListener onSpeakingListener) {
Log.d(TAG_ASKQ, "Initializing TextToSpeech");
this.context = context;
this.onSpeakingListener = onSpeakingListener;
this.webView = webView;
this.webView.addJavascriptInterface(new TTSJavaScriptInterface(), "tts");
initTTS(onInitSucceedListener);
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
private void initTTS(final OnInitSucceedListener onInitSucceedListener) {
tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
Log.d(TAG_ASKQ, "TextToSpeech was initialized successfully.");
initialized = true;
onInitSucceedListener.onInitSucceed();
} else {
Log.e(TAG_ASKQ, "Initilization of TextToSpeech Failed!");
}
}
});
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
}
#Override
public void onDone(String utteranceId) {
Log.e(TAG_ASKQ, "TextToSpeech: " + utteranceId);
onSpeakingListener.onSpeakingEnded();
}
#Override
public void onError(String utteranceId) {
Log.e(TAG_ASKQ, "TextToSpeech: " + utteranceId);
onSpeakingListener.onSpeakingEnded();
}
});
}
/**
* Reads the currently selected text in the WebView.
*/
public void readSelection() {
webView.loadUrl("javascript:tts.speakAloud(window.getSelection().toString());", null);
}
/**
* Starts speaking the WebView content aloud (or stops it if TTS is speaking now).
*/
public void readAloud() {
if (tts.isSpeaking()) {
if (tts.stop() == TextToSpeech.SUCCESS) {
onSpeakingListener.onSpeakingEnded();
}
} else {
Locale locale = LanguageUtils.ISO3ToLocale(ZimContentProvider.getLanguage());
int result;
if (locale == null
|| (result = tts.isLanguageAvailable(locale)) == TextToSpeech.LANG_MISSING_DATA
|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.d(TAG_ASKQ, "TextToSpeech: language not supported: " +
ZimContentProvider.getLanguage() + " (" + locale.getLanguage() + ")");
Toast.makeText(context,
context.getResources().getString(R.string.tts_lang_not_supported),
Toast.LENGTH_LONG).show();
} else {
tts.setLanguage(locale);
// We use JavaScript to get the content of the page conveniently, earlier making some
// changes in the page
webView.loadUrl("javascript:" +
"body = document.getElementsByTagName('body')[0].cloneNode(true);" +
// Remove some elements that are shouldn't be read (table of contents,
// references numbers, thumbnail captions, duplicated title, etc.)
"toRemove = body.querySelectorAll('sup.reference, #toc, .thumbcaption, " +
" title, .navbox');" +
"Array.prototype.forEach.call(toRemove, function(elem) {" +
" elem.parentElement.removeChild(elem);" +
"});" +
"tts.speakAloud(body.innerText);");
}
}
}
/**
* Returns whether the TTS is initialized.
*
* #return <code>true</code> if TTS is initialized; <code>false</code> otherwise
*/
public boolean isInitialized() {
return initialized;
}
/**
* Releases the resources used by the engine.
*
* #see android.speech.tts.TextToSpeech#shutdown()
*/
public void shutdown() {
tts.shutdown();
}
/**
* The listener which is notified when initialization of the TextToSpeech engine is successfully
* done.
*/
public interface OnInitSucceedListener {
public void onInitSucceed();
}
/**
* The listener that is notified when speaking starts or stops (regardless of whether it was a
* result of error, user, or because whole text was read).
*
* Note that the methods of this interface may not be called from the UI thread.
*/
public interface OnSpeakingListener {
public void onSpeakingStarted();
public void onSpeakingEnded();
}
private class TTSJavaScriptInterface {
#JavascriptInterface
#SuppressWarnings("unused")
public void speakAloud(String content) {
String[] lines = content.split("\n");
for (int i = 0; i < lines.length - 1; i++) {
String line = lines[i];
tts.speak(line, TextToSpeech.QUEUE_ADD, null);
}
HashMap<String, String> params = new HashMap<>();
// The utterance ID isn't actually used anywhere, the param is passed only to force
// the utterance listener to be notified
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "kiwixLastMessage");
tts.speak(lines[lines.length - 1], TextToSpeech.QUEUE_ADD, params);
if (lines.length > 0) {
onSpeakingListener.onSpeakingStarted();
}
}
}
}
I am using gwt2.3 celltable.
In my celltable there will be multiple column out of that few columns are dependent.
ex. Name and address columns are dependent
Name and address columns contains my custom selection cells.
In Name column : 1 cell contains jon,tom,steve
when Name cell contains jon then I want to set US & UK in address cell
if user changes Name cell to tom then I want to set india & china in address cell
if user changes Name cell to steve then I want to set japana & bhutan in address cell
I want to change dependent data from address cell when name cells selection changes.
How I can achieve this thing? Any sample code or pointers to do this?
This solution is for GWT 2.5, but it should probably work in 2.3.
I think the best is that you modify your RecordInfo element when you change the selection on the first column. You can do it similar to this in your CustomSelectionCell:
#Override
public void onBrowserEvent(Context context, Element parent, C value, NativeEvent event, ValueUpdater<C> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if (BrowserEvents.CHANGE.equals(event.getType())) {
Xxxxx newValue = getSelectedValueXxxxx();
valueUpdater.update(newValue);
}
}
Then where you use your cell add a fieldUpdater like this, that will update the RecordInfo with the new value and ask to redraw the row:
column.setFieldUpdater(new FieldUpdater<.....>() {
....
recordInfo.setXxxxx(newValue);
cellTable.redrawRow(index);
....
});
This will call the render of the other CustomSelectionCell, in there you will be able to check if the value of the RecordInfo has changed and update the seletion values as needed. Example:
#Override
public void render(Context context, C value, SafeHtmlBuilder sb) {
if (!value.getXxxxx().equals(this.lastValue)) {
this.items = loadItemsForValueXxxx(value.getXxxxx());
}
.... // Your usual render.
}
Be careful when you changed the items to set a default selected item too.
This is my implementation of DynamicSelectionCell.
DynamicSelectionCell allows you to render different options for different rows in the same GWT table.
Use a single DynamicSelectionCell object and use the addOption method to add options for each row. Options are stored in a Map with the Key being the row number.
For each row $i in the table the options stored in the Map for key $i are rendered.
Works on DataGrid, CellTable.
CODE
public class DynamicSelectionCell extends AbstractInputCell<String, String> {
public TreeMap<Integer, List<String>> optionsMap = new TreeMap<Integer, List<String>>();
interface Template extends SafeHtmlTemplates {
#Template("<option value=\"{0}\">{0}</option>")
SafeHtml deselected(String option);
#Template("<option value=\"{0}\" selected=\"selected\">{0}</option>")
SafeHtml selected(String option);
}
private static Template template;
private TreeMap<Integer, HashMap<String, Integer>> indexForOption = new TreeMap<Integer, HashMap<String, Integer>>();
/**
* Construct a new {#link SelectionCell} with the specified options.
*
* #param options the options in the cell
*/
public DynamicSelectionCell() {
super("change");
if (template == null) {
template = GWT.create(Template.class);
}
}
public void addOption(List<String> newOps, int key){
optionsMap.put(key, newOps);
HashMap<String, Integer> localIndexForOption = new HashMap<String, Integer>();
indexForOption.put(ind, localIndexForOption);
refreshIndexes();
}
public void removeOption(int index){
optionsMap.remove(index);
refreshIndexes();
}
private void refreshIndexes(){
int ind=0;
for (List<String> options : optionsMap.values()){
HashMap<String, Integer> localIndexForOption = new HashMap<String, Integer>();
indexForOption.put(ind, localIndexForOption);
int index = 0;
for (String option : options) {
localIndexForOption.put(option, index++);
}
ind++;
}
}
#Override
public void onBrowserEvent(Context context, Element parent, String value,
NativeEvent event, ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
String type = event.getType();
if ("change".equals(type)) {
Object key = context.getKey();
SelectElement select = parent.getFirstChild().cast();
String newValue = optionsMap.get(context.getIndex()).get(select.getSelectedIndex());
setViewData(key, newValue);
finishEditing(parent, newValue, key, valueUpdater);
if (valueUpdater != null) {
valueUpdater.update(newValue);
}
}
}
#Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
// Get the view data.
Object key = context.getKey();
String viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
int selectedIndex = getSelectedIndex(viewData == null ? value : viewData, context.getIndex());
sb.appendHtmlConstant("<select tabindex=\"-1\">");
int index = 0;
try{
for (String option : optionsMap.get(context.getIndex())) {
if (index++ == selectedIndex) {
sb.append(template.selected(option));
} else {
sb.append(template.deselected(option));
}
}
}catch(Exception e){
System.out.println("error");
}
sb.appendHtmlConstant("</select>");
}
private int getSelectedIndex(String value, int ind) {
Integer index = indexForOption.get(ind).get(value);
if (index == null) {
return -1;
}
return index.intValue();
}
}
Varun Tulsian's answer is very good, but the code is incomplete.
The DynamicSelectionCell stores each rows' options in a map. When the cell updates or renders itself, it matches the row index from your Context to its matching row list in your map.
For posterity, see the simplified and updated version below:
public class DynamicSelectionCell extends AbstractInputCell<String, String> {
interface Template extends SafeHtmlTemplates {
#Template("<option value=\"{0}\">{0}</option>")
SafeHtml deselected(String option);
#Template("<option value=\"{0}\" selected=\"selected\">{0}</option>")
SafeHtml selected(String option);
}
private static Template template;
/**
* key: rowIndex
* value: List of options to show for this row
*/
public TreeMap<Integer, List<String>> optionsMap = new TreeMap<Integer, List<String>>();
/**
* Construct a new {#link SelectionCell} with the specified options.
*
*/
public DynamicSelectionCell() {
super("change");
if (template == null) {
template = GWT.create(Template.class);
}
}
public void addOptions(List<String> newOps, int rowIndex) {
optionsMap.put(rowIndex, newOps);
}
public void removeOptions(int rowIndex) {
optionsMap.remove(rowIndex);
}
#Override
public void onBrowserEvent(Context context, Element parent, String value,
NativeEvent event, ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
String type = event.getType();
if ("change".equals(type)) {
Object key = context.getKey();
SelectElement select = parent.getFirstChild().cast();
String newValue = optionsMap.get(context.getIndex()).get(select.getSelectedIndex());
setViewData(key, newValue);
finishEditing(parent, newValue, key, valueUpdater);
if (valueUpdater != null) {
valueUpdater.update(newValue);
}
}
}
#Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
// Get the view data.
Object key = context.getKey();
String viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
int selectedIndex = getSelectedIndex(viewData == null ? value : viewData, context.getIndex());
sb.appendHtmlConstant("<select tabindex=\"-1\">");
int index = 0;
try {
for (String option : optionsMap.get(context.getIndex())) {
if (index++ == selectedIndex) {
sb.append(template.selected(option));
} else {
sb.append(template.deselected(option));
}
}
} catch (Exception e) {
System.out.println("error");
}
sb.appendHtmlConstant("</select>");
}
private int getSelectedIndex(String value, int rowIndex) {
if (optionsMap.get(rowIndex) == null) {
return -1;
}
return optionsMap.get(rowIndex).indexOf(value);
}
}
Or you could do something like creating a custom cell, which has methods you can call in the getValue of that particular column
say
final DynamicSelectionCell selection = new DynamicSelectionCell("...");
Column<GraphFilterCondition, String> operandColumn=new Column<GraphFilterCondition, String>(selection) {
#Override
public String getValue(FilterCondition object) {
if(object.getWhereCol()!=null){
((DynamicSelectionCell)this.getCell()).addOptions(new String[]{">","<",">="});
}
if(object.getWhereCondition()!=null){
return object.getWhereCondition().getGuiName();
}
return "";
}
};
This should work I guess.
Also check this other question