XML on Fragments를 사용하여 버튼 클릭을 처리하는 방법 Click in Fragments
사전 허니콤(Android 3), 각 활동은 다음을 통해 버튼 클릭을 처리하도록 등록되었습니다.onClick
a XML: "XML":
android:onClick="myClickMethod"
안에서는 해당방내사수있다니습용할서에법을 사용할 수 .view.getId()
버튼 로직을 수행하기 위한 스위치 문.
허니콤의 소개로 저는 이러한 활동을 여러 활동 내에서 재사용할 수 있는 단편으로 나누고 있습니다.버튼 동작의 대부분은 작업에 독립적이며 코드는 이전(1.6 이전) 방법을 사용하지 않고 Fragments 파일 내에 상주하고 싶습니다.OnClickListener
각 버튼에 대해
final Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
문제는 레이아웃이 부풀려졌을 때 개별 조각이 아니라 버튼 클릭을 받는 것은 호스팅 활동이라는 것입니다.다음 중 어느 것에 대한 좋은 접근법이 있습니까?
- 버튼 클릭을 받으려면 단편을 등록하시겠습니까?
- 클릭 이벤트를 활동에서 자신이 속한 단편으로 전달하시겠습니까?
클릭 이벤트 처리를 위해 다음 솔루션을 사용하는 것을 선호합니다.활동 및 조각에도 사용할 수 있습니다.
public class StartFragment extends Fragment implements OnClickListener{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_start, container, false);
Button b = (Button) v.findViewById(R.id.StartButton);
b.setOnClickListener(this);
return v;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.StartButton:
...
break;
}
}
}
이렇게 하면 됩니다.
활동:
Fragment someFragment;
//...onCreate etc instantiating your fragments
public void myClickMethod(View v) {
someFragment.myClickMethod(v);
}
조각:
public void myClickMethod(View v) {
switch(v.getId()) {
// Just like you were doing
}
}
파편이 재사용될 수 있도록 커플링을 줄이기를 원했던 @Amen에 대한 응답.
인터페이스:
public interface XmlClickable {
void myClickMethod(View v);
}
활동:
XmlClickable someFragment;
//...onCreate, etc. instantiating your fragments casting to your interface.
public void myClickMethod(View v) {
someFragment.myClickMethod(v);
}
조각:
public class SomeFragment implements XmlClickable {
//...onCreateView, etc.
@Override
public void myClickMethod(View v) {
switch(v.getId()){
// Just like you were doing
}
}
제가 생각하는 문제는 뷰가 여전히 단편적인 것이 아니라 활동이라는 것입니다.조각은 자체적으로 독립적인 보기가 없으며 상위 활동 보기에 첨부됩니다.그렇기 때문에 이벤트는 조각이 아닌 활동으로 끝납니다.안타깝지만, 이 작업을 위해서는 코드가 필요할 것 같습니다.
변환하는 동안 기존 이벤트 처리기를 호출하는 클릭 수신기를 추가했습니다.
예를 들어:
final Button loginButton = (Button) view.findViewById(R.id.loginButton);
loginButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
onLoginClicked(v);
}
});
최근에 작업 컨텍스트에 메서드를 추가하거나 OnClickListener를 구현하지 않고도 이 문제를 해결했습니다.그것이 "유효한" 해결책인지는 모르겠지만, 효과가 있습니다.
기준: https://developer.android.com/tools/data-binding/guide.html#binding_events
데이터 바인딩을 사용하여 수행할 수 있습니다.fragment instance를 변수로 추가하기만 하면, 어떤 메소드든 onClick과 연결할 수 있습니다.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.testapp.fragments.CustomFragment">
<data>
<variable android:name="fragment" android:type="com.example.testapp.fragments.CustomFragment"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_place_black_24dp"
android:onClick="@{() -> fragment.buttonClicked()}"/>
</LinearLayout>
</layout>
그리고 조각 연결 코드는...
public class CustomFragment extends Fragment {
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_person_profile, container, false);
FragmentCustomBinding binding = DataBindingUtil.bind(view);
binding.setFragment(this);
return view;
}
...
}
버터나이프는 아마도 혼란스러운 문제에 대한 최고의 해결책일 것입니다.주석 처리기를 사용하여 이른바 "오래된 방법" 상용판 코드를 생성합니다.
그러나 onClick 방법은 사용자 지정 인플레이터와 함께 계속 사용할 수 있습니다.
사용방법
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup cnt, Bundle state) {
inflater = FragmentInflatorFactory.inflatorFor(inflater, this);
return inflater.inflate(R.layout.fragment_main, cnt, false);
}
실행
public class FragmentInflatorFactory implements LayoutInflater.Factory {
private static final int[] sWantedAttrs = { android.R.attr.onClick };
private static final Method sOnCreateViewMethod;
static {
// We could duplicate its functionallity.. or just ignore its a protected method.
try {
Method method = LayoutInflater.class.getDeclaredMethod(
"onCreateView", String.class, AttributeSet.class);
method.setAccessible(true);
sOnCreateViewMethod = method;
} catch (NoSuchMethodException e) {
// Public API: Should not happen.
throw new RuntimeException(e);
}
}
private final LayoutInflater mInflator;
private final Object mFragment;
public FragmentInflatorFactory(LayoutInflater delegate, Object fragment) {
if (delegate == null || fragment == null) {
throw new NullPointerException();
}
mInflator = delegate;
mFragment = fragment;
}
public static LayoutInflater inflatorFor(LayoutInflater original, Object fragment) {
LayoutInflater inflator = original.cloneInContext(original.getContext());
FragmentInflatorFactory factory = new FragmentInflatorFactory(inflator, fragment);
inflator.setFactory(factory);
return inflator;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if ("fragment".equals(name)) {
// Let the Activity ("private factory") handle it
return null;
}
View view = null;
if (name.indexOf('.') == -1) {
try {
view = (View) sOnCreateViewMethod.invoke(mInflator, name, attrs);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof ClassNotFoundException) {
return null;
}
throw new RuntimeException(e);
}
} else {
try {
view = mInflator.createView(name, null, attrs);
} catch (ClassNotFoundException e) {
return null;
}
}
TypedArray a = context.obtainStyledAttributes(attrs, sWantedAttrs);
String methodName = a.getString(0);
a.recycle();
if (methodName != null) {
view.setOnClickListener(new FragmentClickListener(mFragment, methodName));
}
return view;
}
private static class FragmentClickListener implements OnClickListener {
private final Object mFragment;
private final String mMethodName;
private Method mMethod;
public FragmentClickListener(Object fragment, String methodName) {
mFragment = fragment;
mMethodName = methodName;
}
@Override
public void onClick(View v) {
if (mMethod == null) {
Class<?> clazz = mFragment.getClass();
try {
mMethod = clazz.getMethod(mMethodName, View.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"Cannot find public method " + mMethodName + "(View) on "
+ clazz + " for onClick");
}
}
try {
mMethod.invoke(mFragment, v);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}
}
}
나는 에서 클릭 핸들링을 코드에서 .onClick
XML에서 속성을 지정할 수 있습니다.
이렇게 하면 활동을 조각으로 마이그레이션할 때 훨씬 더 쉬워집니다.클릭 핸들러를 호출할 수 있습니다(이전 설정됨).android:onClick
각 XML에서 ) 각서에직case
블록으로 막다
findViewById(R.id.button_login).setOnClickListener(clickListener);
...
OnClickListener clickListener = new OnClickListener() {
@Override
public void onClick(final View v) {
switch(v.getId()) {
case R.id.button_login:
// Which is supposed to be called automatically in your
// activity, which has now changed to a fragment.
onLoginClick(v);
break;
case R.id.button_logout:
...
}
}
}
하는 것에 에 이것은 단으로클릭처리것과보제이에보다 더 간단해 .android:onClick
.
다음과 같은 방법이 방법은 다음과 같습니다.
1. 다음과 같은 기본 조각을 만듭니다.
public abstract class BaseFragment extends Fragment implements OnClickListener
2.사용
public class FragmentA extends BaseFragment
대신에
public class FragmentA extends Fragment
3. 활동 중:
public class MainActivity extends ActionBarActivity implements OnClickListener
그리고.
BaseFragment fragment = new FragmentA;
public void onClick(View v){
fragment.onClick(v);
}
도움이 되길 바랍니다.
제 사용 사례에는 클릭 방식 하나로 연결하는 데 필요한 50개의 이상한 이미지 보기가 있습니다.내 해결책은 fragment 내부의 뷰를 루프하고 각 뷰에 동일한 onclick listener를 설정하는 것입니다.
final View.OnClickListener imageOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
chosenImage = ((ImageButton)v).getDrawable();
}
};
ViewGroup root = (ViewGroup) getView().findViewById(R.id.imagesParentView);
int childViewCount = root.getChildCount();
for (int i=0; i < childViewCount; i++){
View image = root.getChildAt(i);
if (image instanceof ImageButton) {
((ImageButton)image).setOnClickListener(imageOnClickListener);
}
}
제가 답을 보니 그들은 왠지 오래된 것 같습니다.최근 구글은 클릭이나 xml에 할당하면 훨씬 다루기 쉬운 데이터 바인딩을 도입했습니다.
다음은 이 문제를 처리하는 방법을 확인할 수 있는 좋은 예입니다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.Handlers"/>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
</LinearLayout>
</layout>
DataBinding에 대한 매우 좋은 튜토리얼도 여기에서 찾을 수 있습니다.
콜백을 XML 레이아웃의 속성으로 정의할 수 있습니다.사용자 정의 Android 위젯에 대한 사용자 정의 XML 속성 문서에서 사용자 정의 위젯에 대해 수행하는 방법을 보여 줍니다.신용은 케빈 디온에게 돌아옵니다 :)
기본 Fragment 클래스에 스타일 가능한 속성을 추가할 수 있는지 조사 중입니다.
기본 아이디어는 OnClick 콜백을 처리할 때 View가 구현하는 기능과 동일한 기능을 사용하는 것입니다.
, 룬델의대더하여블에답,여▁adding블,
클릭 횟수가 많은 조각이 더 많은 경우:
활동:
Fragment someFragment1 = (Fragment)getFragmentManager().findFragmentByTag("someFragment1 ");
Fragment someFragment2 = (Fragment)getFragmentManager().findFragmentByTag("someFragment2 ");
Fragment someFragment3 = (Fragment)getFragmentManager().findFragmentByTag("someFragment3 ");
...onCreate etc instantiating your fragments
public void myClickMethod(View v){
if (someFragment1.isVisible()) {
someFragment1.myClickMethod(v);
}else if(someFragment2.isVisible()){
someFragment2.myClickMethod(v);
}else if(someFragment3.isVisible()){
someFragment3.myClickMethod(v);
}
}
파편 속:
public void myClickMethod(View v){
switch(v.getid()){
// Just like you were doing
}
}
안드로이드를 사용하여 xml에 등록하는 경우:클릭=클릭 시 해당 단편이 속한 컨텍스트(get Activity())에 따라 존경받는 활동에 콜백이 제공됩니다.활동에서 이러한 방법을 찾을 수 없는 경우 시스템에서 예외를 발생시킵니다.
분리된 이벤트에 대해 EventBus를 사용하는 것을 고려해 볼 수 있습니다.여러분은 사건을 매우 쉽게 들을 수 있습니다.또한 runOnUiThread를 호출하는 대신 UI 스레드에서 이벤트가 수신되고 있는지 확인할 수 있습니다.모든 이벤트 구독에 대해 직접 확인)
https://github.com/greenrobot/EventBus
Github에서:
Activities, Fragments, Threads, Services 등 간의 통신을 단순화하는 Android 최적화 이벤트 버스코드 감소, 품질 향상
저는 Adjorn Linkz의 답변에 추가하고 싶습니다.
여러 핸들러가 필요한 경우 람다 참조를 사용할 수 있습니다.
void onViewCreated(View view, Bundle savedInstanceState)
{
view.setOnClickListener(this::handler);
}
void handler(View v)
{
...
}
여기서의 비결은handler
메소드의 서명 일치View.OnClickListener.onClick
서명.이렇게 하면, 당신은 그것이 필요하지 않을 것입니다.View.OnClickListener
인터페이스
또한 스위치 문이 필요하지 않습니다.
안타깝게도 이 방법은 단일 방법 또는 람다가 필요한 인터페이스로만 제한됩니다.
데이터 바인딩에 의존하는 몇 가지 훌륭한 답변을 발견했지만 XML에서 프래그먼트가 없는 레이아웃 정의를 허용하면서 프래그먼트 해결을 가능하게 한다는 점에서 그 접근 방식이 완전히 적용될 것이라고는 생각하지 못했습니다.
따라서 데이터 바인딩을 사용한다고 가정하면 일반적인 솔루션을 제안할 수 있습니다. 약간 길지만 확실히 작동합니다(몇 가지 주의 사항).
1단계: 맞춤형 OnClick 구현
이렇게 하면 탭 온 보기와 관련된 컨텍스트(예: 버튼)를 통해 단편 인식 검색이 실행됩니다.
// CustomOnClick.kt
@file:JvmName("CustomOnClick")
package com.example
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import java.lang.reflect.Method
fun onClick(view: View, methodName: String) {
resolveOnClickInvocation(view, methodName)?.invoke(view)
}
private data class OnClickInvocation(val obj: Any, val method: Method) {
fun invoke(view: View) {
method.invoke(obj, view)
}
}
private fun resolveOnClickInvocation(view: View, methodName: String): OnClickInvocation? =
searchContexts(view) { context ->
var invocation: OnClickInvocation? = null
if (context is Activity) {
val activity = context as? FragmentActivity
?: throw IllegalStateException("A non-FragmentActivity is not supported (looking up an onClick handler of $view)")
invocation = getTopFragment(activity)?.let { fragment ->
resolveInvocation(fragment, methodName)
}?: resolveInvocation(context, methodName)
}
invocation
}
private fun getTopFragment(activity: FragmentActivity): Fragment? {
val fragments = activity.supportFragmentManager.fragments
return if (fragments.isEmpty()) null else fragments.last()
}
private fun resolveInvocation(target: Any, methodName: String): OnClickInvocation? =
try {
val method = target.javaClass.getMethod(methodName, View::class.java)
OnClickInvocation(target, method)
} catch (e: NoSuchMethodException) {
null
}
private fun <T: Any> searchContexts(view: View, matcher: (context: Context) -> T?): T? {
var context = view.context
while (context != null && context is ContextWrapper) {
val result = matcher(context)
if (result == null) {
context = context.baseContext
} else {
return result
}
}
return null
}
참고: 원래 Android 구현을 기반으로 합니다(https://android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/android/view/View.java#3025) 참조).
2단계: 레이아웃 파일의 선언적 응용 프로그램
그런 다음 데이터 바인딩에서 XML을 인식합니다.
<layout>
<data>
<import type="com.example.CustomOnClick"/>
</data>
<Button
android:onClick='@{(v) -> CustomOnClick.onClick(v, "myClickMethod")}'
</Button>
</layout>
주의사항
- '현대적'이라고 가정합니다.
FragmentActivity
기본 구현 - 스택에서 "최상위"(즉, 마지막) 조각의 조회 방법만 사용할 수 있습니다(필요한 경우 수정할 수 있지만).
이것은 저에게 효과가 있었습니다: (Android 스튜디오)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.update_credential, container, false);
Button bt_login = (Button) rootView.findViewById(R.id.btnSend);
bt_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
System.out.println("Hi its me");
}// end onClick
});
return rootView;
}// end onCreateView
최상의 솔루션 IMHO:
단편적으로:
protected void addClick(int id) {
try {
getView().findViewById(id).setOnClickListener(this);
} catch (Exception e) {
e.printStackTrace();
}
}
public void onClick(View v) {
if (v.getId()==R.id.myButton) {
onMyButtonClick(v);
}
}
그런 다음 ViewStateRestored의 Fragment에서 다음 작업을 수행합니다.
addClick(R.id.myButton);
활동에서 다음을 사용해야 하는 콜백을 수신하고 있어야 합니다.
mViewPagerCloth.setOnClickListener((YourActivityName)getActivity());
조각이 콜백을 수신하도록 하려면 다음을 수행합니다.
mViewPagerCloth.setOnClickListener(this);
및 구현onClickListener
프래그먼트의 인터페이스
다음 솔루션을 따르는 것이 더 나을 수 있습니다.레이아웃이 다음 위치에 있습니다.fragment_my.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="listener"
type="my_package.MyListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/moreTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> listener.onClick()}"
android:text="@string/login"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
그리고 그 파편은 다음과 같습니다.
class MyFragment : Fragment(), MyListener {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return FragmentMyBinding.inflate(
inflater,
container,
false
).apply {
lifecycleOwner = viewLifecycleOwner
listener = this@MyFragment
}.root
}
override fun onClick() {
TODO("Not yet implemented")
}
}
interface MyListener{
fun onClick()
}
언급URL : https://stackoverflow.com/questions/6091194/how-to-handle-button-clicks-using-the-xml-onclick-within-fragments
'programing' 카테고리의 다른 글
루비: 루비 보석의 특정 버전을 어떻게 설치합니까? (0) | 2023.06.04 |
---|---|
맥 OS에 노코기리를 설치하면 실패하고 libiconv가 누락되는 이유는 무엇입니까? (0) | 2023.06.04 |
Git에서의 병합이 SVN에서의 병합보다 어떻게 그리고/또는 왜 더 낫습니까? (0) | 2023.06.04 |
Struct를 사용해야 하는 경우와OpenStruct? (0) | 2023.06.04 |
delete_all vs destroy_all? (0) | 2023.06.04 |