OnMeasure (): wrap_content, как узнать размер обертывания?

Я сделал собственный вид с onDraw() который рисует растровое изображение на холсте. Когда я укажу, что я хочу, чтобы он wrap_content в файле макета, он по-прежнему заполняет весь экран. onMeasure() говорит следующее:

По умолчанию базовый класс меры измеряет размер фона, если только MeasureSpec разрешает больший размер. Подклассы должны переопределять onMeasure (int, int), чтобы обеспечить лучшее измерение их содержимого.

Хорошо, так что я знаю, что мне нужно переопределить onMeasure() и работать с MeasureSpec . Согласно этому ответу

UNSPECIFIED означает, что для параметра layout_width или layout_height установлено значение wrap_content. Вы можете быть в любом размере.

Теперь я добираюсь до своей проблемы, как мне на onMeasure() измерять растровое изображение, которое еще не создано, и измерять / обертывать его? Я знаю, что другие представления Android ДОЛЖНЫ что-то делать, потому что они не блокируют весь экран, если он установлен в wrap_content. Заранее спасибо!

Solutions Collecting From Web of "OnMeasure (): wrap_content, как узнать размер обертывания?"

Если вы не можете измерить растровое изображение до вызова onMeasure, вы можете вернуть нулевой размер до тех пор, пока битмап не будет загружен. Как только он будет загружен, недействительность родительской ViewGroup, чтобы принудительно выполнить другую меру (не могу вспомнить, если invalidate () в самом представлении заставит onMeasure).

Это порядок, в котором используются эти обычно используемые методы просмотра:

 1. Constructor // choose your desired size 2. onMeasure // parent will determine if your desired size is acceptable 3. onSizeChanged 4. onLayout 5. onDraw // draw your view content at the size specified by the parent 

Выбор желаемого размера

Если ваш взгляд может быть любого размера, который он хотел, какой размер он выберет? Это будет ваш размер wrap_content и будет зависеть от содержимого вашего пользовательского представления. Примеры:

  • Если ваш пользовательский вид является изображением, то ваш желаемый размер, вероятно, будет размером пикселя растрового изображения плюс любое дополнение. (Вы несете ответственность за то, чтобы подстраивать ваши вычисления при выборе размера и рисования содержимого.)
  • Если пользовательский вид является аналоговым часом, то желаемым размером может быть какой-то размер по умолчанию, который будет хорошо смотреться. (Вы всегда можете получить размер dp для px для устройства.)

Если ваш желаемый размер использует тяжелые вычисления, тогда сделайте это в своем конструкторе. В противном случае вы можете просто назначить его в onMeasure . ( onMeasure , onLayout и onDraw могут быть вызваны несколько раз, поэтому здесь нехорошо выполнять тяжелую работу).

Согласование окончательного размера

onMeasure – это место, где ребенок сообщает родителям, насколько он хотел бы быть, и родитель решает, приемлемо ли это. Этот метод часто вызывается несколько раз, каждый раз проходящий в разных требованиях к размеру, видя, может ли быть достигнут некоторый компромисс. В конце концов, однако, ребенок должен соблюдать требования к размеру родителя.

Я всегда возвращаюсь к этому ответу, когда мне нужно переосмыслить, как настроить мой onMeasure :

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int desiredWidth = 100; int desiredHeight = 100; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; //Measure Width if (widthMode == MeasureSpec.EXACTLY) { //Must be this size width = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { //Can't be bigger than... width = Math.min(desiredWidth, widthSize); } else { //Be whatever you want width = desiredWidth; } //Measure Height if (heightMode == MeasureSpec.EXACTLY) { //Must be this size height = heightSize; } else if (heightMode == MeasureSpec.AT_MOST) { //Can't be bigger than... height = Math.min(desiredHeight, heightSize); } else { //Be whatever you want height = desiredHeight; } //MUST CALL THIS setMeasuredDimension(width, height); } 

В приведенном выше примере требуемая ширина и высота были установлены только на некоторые значения по умолчанию. Вы могли бы вместо этого рассчитать их заранее и установить их здесь, используя переменную-член класса.

Использование выбранного размера

После onMeasure размер вашего представления известен. Этот размер может быть или не быть тем, что вы просили, но с этим вы должны работать. Используйте этот размер, чтобы нарисовать содержимое на вашем представлении в onDraw .

Заметки

  • В любое время, когда вы вносите изменения в свое представление, которое влияет на внешний вид, но не на размер, вызовите invalidate() . Это вызовет onDraw (но не все другие предыдущие методы).
  • В любое время, когда вы делаете изменения в своем представлении, которые влияют на размер, затем вызывайте requestLayout() . Это начнет процесс измерения и рисования снова с onMeasure . Обычно это сочетается с вызовом invalidate() .
  • Если по какой-то причине вы действительно не можете определить подходящий желаемый размер заранее, я полагаю, вы можете сделать это как @nmw и просто запросить нулевую ширину, нулевую высоту. Затем запросите макет (а не просто invalidate() ) после того, как все будет загружено. Это похоже на немного отходов, потому что вам требуется, чтобы вся иерархия представлений была выложена дважды подряд.