Skip to content
This repository has been archived by the owner on Oct 17, 2023. It is now read-only.

Add AppCompat inflation inject sample #69

Open
JakeWharton opened this issue Oct 26, 2018 · 5 comments
Open

Add AppCompat inflation inject sample #69

JakeWharton opened this issue Oct 26, 2018 · 5 comments

Comments

@JakeWharton
Copy link
Contributor

With test. I have this working in a side-project, but we should show it here and write a test to make sure it continues working.

@JakeWharton
Copy link
Contributor Author

(Someone reminded me of this at the talk. Thanks, person!)

@abyrnes
Copy link
Contributor

abyrnes commented Nov 11, 2018

I did the following and it seems to be working okay

class InflationInjectContextWrapper constructor(
  newBase: Context,
  private val inflationInjectFactory: InflationInjectFactory
) : ContextWrapper(newBase) {
  private var inflater: LayoutInflater? = null

  override fun getSystemService(name: String): Any? {
    if (LAYOUT_INFLATER_SERVICE == name) {
      if (inflater == null) {
        inflater = LayoutInflater.from(baseContext)
            .cloneInContext(this)
            .apply {
              factory = FactoryWrapper(factory, inflationInjectFactory)
            }
      }
      return inflater!!
    }
    return super.getSystemService(name)
  }

  private class FactoryWrapper(
    private val factory: LayoutInflater.Factory?,
    private val inflationInjectFactory: InflationInjectFactory
  ) : LayoutInflater.Factory {
    override fun onCreateView(
      name: String,
      context: Context,
      attrs: AttributeSet?
    ): View? {
      return inflationInjectFactory.onCreateView(name, context, attrs)
          ?: factory?.onCreateView(name, context, attrs)
    }
  }
}

@dhruvj
Copy link

dhruvj commented Apr 14, 2019

Any updates? I get

A factory has already been set on this LayoutInflater

when setting Layout Factory in AppCompatActivity.

@abyrnes
Copy link
Contributor

abyrnes commented Apr 14, 2019

@dhruvj Did you try the sample that I posted above?

The problem with using the factory with app compat is there as already a factory set on the inflater by default, which is what allows the app compat version of certain widgets to be inflated instead of their standard counterpart. e.g., TextView gets inflated as AppCompatTextView. LayoutInflater does not allow you to change the factory or factory2 without first making a copy of it via cloneInContext. Once that's done you're free to set a new factory on it. Just be aware that it'll replace the original one which means you lose a lot of the benefits. This issue is solved with the code snippet I posted.

@mars885
Copy link

mars885 commented May 14, 2020

@abyrnes Yes, your solution is working perfectly, although I believe it would be helpful to demonstrate to newcomers where and how to use InflationInjectContextWrapper class you've provided. Something like the following should suffice:

public final class MainActivity extends Activity {

    @Override public void attachBaseContext(Context newBase) {
        super.attachBaseContext(InflationInjectContextWrapper(
            newBase,
            DaggerMainActivity_MainComponent.create().factory()
        ));
    }

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.custom_view);
    }

    @Component(modules = ViewModule.class)
    interface MainComponent {
        InflationInjectFactory factory();
    }

}

If you have a BaseActivity, then you can put it there instead and forget about it in subclasses. You do not need to override anything in fragments, since they contain a reference to a host activity and will use the shared inflater. Hope it helps someone.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants