Как реализовать TapTargetView на RecyclerView?
Вот мой fetchData.java (Activity)
package com.example.expirytracker;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.getkeepsafe.taptargetview.TapTarget;
import com.getkeepsafe.taptargetview.TapTargetSequence;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
public class fetchData extends AppCompatActivity {
RecyclerView recyclerView;
ArrayList<model> dataHolder;
FloatingActionButton addEntry;
Cursor cursor;
long expirydaysleft;
myAdapter.myviewholder myviewholder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fetch_data);
recyclerView = findViewById(R.id.recview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
addEntry = findViewById(R.id.addEntry);
cursor = new DBHelper(this).readalldata();
dataHolder = new ArrayList<>();
addEntry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent addEntry = new Intent(fetchData.this, AddItem.class);
startActivity(addEntry);
}
});
while (cursor.moveToNext()) {
long itemaddedondate = cursor.getLong(4);
long expirydatecountingfromitemaddedon = cursor.getLong(3);
long msDiff = Calendar.getInstance().getTimeInMillis() - itemaddedondate;
long daysDiffToday = TimeUnit.MILLISECONDS.toDays(msDiff);
expirydaysleft = (expirydatecountingfromitemaddedon - daysDiffToday);
model modelobj = new model(cursor.getString(1), cursor.getString(2),expirydaysleft,cursor.getInt(0));
dataHolder.add(modelobj);
}
myAdapter adapter = new myAdapter(dataHolder, fetchData.this,this);
recyclerView.setAdapter(adapter);
new TapTargetSequence(this)
.targets(
TapTarget.forView(recyclerView.findViewById(R.id.deleteEntry), "Click to Save Data","After filling the above fields, click on the save button to save the data into your list.")
.cancelable(false)
.outerCircleColor(R.color.red)
.transparentTarget(true),
TapTarget.forView(recyclerView.findViewById(R.id.editEntry), "Click to get the list of data", "Fetch all the data by clicking on show button. you will be able to delete and modify the data.")
.cancelable(false)
.outerCircleColor(R.color.red)
.transparentTarget(true))
.listener(new TapTargetSequence.Listener() {
@Override
public void onSequenceFinish() {
Toast.makeText(getApplicationContext(), "TapTargetView Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onSequenceStep(TapTarget lastTarget, boolean targetClicked) {
// Perform action for the current target
}
@Override
public void onSequenceCanceled(TapTarget lastTarget) {
// Boo
}
})
.start();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
recreate();
}
}
}
myAdapter.java (класс адаптера)
package com.example.expirytracker;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.nfc.Tag;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import static android.content.ContentValues.TAG;
public class myAdapter extends RecyclerView.Adapter<myAdapter.myviewholder>
{
ArrayList<model> dataHolder;
Activity activity;
DBHelper MyDB;
int id;
int mYear, mMonth, mDay;
DBHelper DB;
long msDiff, daysDiff;
long itemaddedon;
EditText updateditemname,updateddate;
fetchData fd;
Context context;
public myAdapter(ArrayList<model> dataHolder, Activity activity, Context context) {
this.context = context;
this.dataHolder = dataHolder;
this.activity = activity;
}
@NonNull
@Override
public myviewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.singlerow,parent,false);
Log.i(TAG,"OnCreateViewHolder Invoked");
return new myviewholder(view);
}
@Override
public void onBindViewHolder(@NonNull @NotNull myAdapter.myviewholder holder, int position)
{
holder.ditemname.setText(dataHolder.get(position).getItemname());
holder.dexpirydate.setText(dataHolder.get(position).getSelecteddate());
if (dataHolder.get(position).getExpirydate() == 0) {
holder.dexpirydays.setText("Expiring Today");
} else if (dataHolder.get(position).getExpirydate() < 0) {
holder.dexpirydays.setText("Expired.");
}
else {
holder.dexpirydays.setText("Expiring In: " + dataHolder.get(position).getExpirydate() + "Days.");
}
holder.ddeleteEntry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle("Confirmation !");
builder.setMessage("Are you sure you want to Delete this Entry ?");
builder.setIcon(android.R.drawable.ic_menu_delete);
builder.setCancelable(false);
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
id = dataHolder.get(position).getId();
DBHelper dbHelper = new DBHelper(v.getContext());
dbHelper.deleteData(id);
dataHolder.remove(position);
notifyItemRemoved(position);
}
});
builder.setNegativeButton("No", null);
builder.show();
}
});
holder.deditEntry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent lastData = new Intent(context, updateDetails.class);
lastData.putExtra("lastItemName", dataHolder.get(position).getItemname());
lastData.putExtra("lastExpiryDate", dataHolder.get(position).getSelecteddate());
lastData.putExtra("id", dataHolder.get(position).getId());
activity.startActivityForResult(lastData, 1);
/*
Dialog dialog = new Dialog(v.getContext());
dialog.setContentView(R.layout.customeditalertdialog);
dialog.setTitle("Update Details");
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
dialog.setCancelable(false);
updateditemname = dialog.findViewById(R.id.updateitemname);
updateddate = dialog.findViewById(R.id.updatedDate);
Button update = dialog.findViewById(R.id.update);
updateddate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v == updateddate) {
// Get Current Date
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
DatePickerDialog datePickerDialog = new DatePickerDialog(v.getContext(),
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
updateddate.setText(year + "-" + (monthOfYear + 1) + "-" + dayOfMonth);
}
}, mYear, mMonth, mDay);
datePickerDialog.show();
}
}
});
update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DB = new DBHelper(v.getContext());
Date date = Date.valueOf(updateddate.getText().toString());
msDiff = date.getTime() - Calendar.getInstance().getTimeInMillis();
daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff) + 1;
String itemname = updateditemname.getText().toString();
String expdate = updateddate.getText().toString();
itemaddedon = Calendar.getInstance().getTimeInMillis();
java.sql.Date sqlDate = java.sql.Date.valueOf(expdate);
id = dataHolder.get(position).getId();
Boolean update = DB.updateDetails(id, itemname, sqlDate, daysDiff, itemaddedon);
if(update==true){
Toast.makeText(v.getContext(),
"Details Updated Successfully", Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
dialog.cancel();
}else{
Toast.makeText(v.getContext(),
"Details Updation Failed. Please Try Again Later", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
}
});
dialog.show(); */
}
});
Log.i(TAG,"OnBindViewHolder Invoked");
}
@Override
public int getItemCount() {
return dataHolder.size();
}
class myviewholder extends RecyclerView.ViewHolder
{
TextView ditemname,dexpirydate, dexpirydays;
Button ddeleteEntry, deditEntry;
public myviewholder(@NonNull View itemView)
{
super(itemView);
ditemname=itemView.findViewById(R.id.displayitemname);
dexpirydate=itemView.findViewById(R.id.displaydate);
dexpirydays=itemView.findViewById(R.id.expiryindays);
ddeleteEntry = itemView.findViewById(R.id.deleteEntry);
deditEntry = itemView.findViewById(R.id.editEntry);
}
}
}
Singlerow.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center_horizontal"
app:cardUseCompatPadding="true"
app:cardCornerRadius="6dp"
android:elevation="4dp"
android:layout_margin="5dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@color/cardbg"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/displayitemname"
android:layout_width="204dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="Item Name"
android:textSize="25sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/displaydate"
android:layout_width="204dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="Expiry Date"
android:textSize="25sp" />
<TextView
android:id="@+id/expiryindays"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="TextView"
android:textColor="#F40F0F" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/deleteEntry"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:backgroundTint="#E36D6D"
android:text="Delete" />
<Button
android:id="@+id/editEntry"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:backgroundTint="#E36D6D"
android:text="Edit"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
Как реализовать TapTargetView в действии fetchData, которое является recyclerView?
Приведенный выше код не работает, когда не работает. Когда я вызываю это действие fetchdata, оно показывает мне ошибку:
E/AndroidRuntime: FATAL EXCEPTION: основной процесс: com.example.expirytracker, PID: 28859java.lang.RuntimeException: невозможно запустить действие ComponentInfo{com.example.expirytracker/com.example.expirytracker.fetchData}: java.lang.IllegalArgumentException: задано нулевое представление для таргетинга на android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3374) на android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3513) на android.app.servertransaction.LaunchActivityItemctivityItem (LaunchActivityItem.execute. java: 83) на android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) на android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) на android.app.ActivityThread $H.handleMessage (Activity .java: 2109) на android.os.Handler.dispatchMessage(Handler.java:107) на android.os.Looper.loop(Looper.java:214) в android.app.ActivityThread.main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:516) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) Вызвано: java.lang.IllegalArgumentException: задано нулевое представление для целевого объекта com.getkeepsafe.taptargetview. ViewTapTarget. (ViewTapTarget.java:31) в com.getkeepsafe.taptargetview.TapTarget.forView(TapTarget.java:175) в com.example.expirytracker.fetchData.onCreate(fetchData.java:62) в android.app.Activity. PerformCreate(Activity.java:7815) в android.app.Activity.performCreate(Activity.java:7804) в android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1318) в android.app.ActivityThread.performLaunchActivity (ActivityThread.java : 3349) в android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3513) на android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) на android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) на android. .TransactionExecutor.execute(TransactionExecutor.java:95) в android.app.ActivityThread $H.handleMessage (ActivityThread.java:2109) в android.os.Handler.dispatchMessage(Handler.java:107) в android.os.Looper. цикл (Looper.java:214) в android.app.ActivityThread.main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller. запустить (RuntimeInit.java:516) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:83) в android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) в android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.execute (TransactionExecutor.execute (TransactionExecutor.exe) .ActivityThread $H.handleMessage (ActivityThread.java:2109) в android.os.Handler.dispatchMessage(Handler.java:107) в android.os.Looper.loop(Looper.java:214) в android.app.ActivityThread. main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:516) в com.android.internal. os.ZygoteInit.main (ZygoteInit.java:950)servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:83) в android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) в android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.execute (TransactionExecutor.execute (TransactionExecutor.exe) .ActivityThread $H.handleMessage (ActivityThread.java:2109) в android.os.Handler.dispatchMessage(Handler.java:107) в android.os.Looper.loop(Looper.java:214) в android.app.ActivityThread. main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:516) в com.android.internal. os.ZygoteInit.main (ZygoteInit.java:950)executeCallbacks (TransactionExecutor.java:135) на android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) на android.app.ActivityThread $H.handleMessage (ActivityThread.java:2109) на android.os.Handler.dispatchMessage(Handler.java:107) в android.os.Looper.loop(Looper.java:214) в android.app.ActivityThread.main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод ) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:516) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)executeCallbacks (TransactionExecutor.java:135) на android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) на android.app.ActivityThread $H.handleMessage (ActivityThread.java:2109) на android.os.Handler.dispatchMessage(Handler.java:107) в android.os.Looper.loop(Looper.java:214) в android.app.ActivityThread.main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод ) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:516) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)214) в android.app.ActivityThread.main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit.java: 516) на com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)214) в android.app.ActivityThread.main (ActivityThread.java:7682) в java.lang.reflect.Method.invoke (собственный метод) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit.java: 516) на com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)