Страницы

Поиск по вопросам

суббота, 11 января 2020 г.

Перебор нескольких массивов

#java #массивы


Имеется 3 строковых массива разной длины.
Как перебрать все 3 массива через 1 цикл?
    


Ответы

Ответ 1



String[] a = new String[10]; String[] b = new String[20]; String[] c = new String[30]; for (int i = 0; i < Math.max(a.length, Math.max(b.length, c.length)); i++) { if(i < a.length) { // операция массивом a } if(i < b.length) { // операция массивом b } if(i < c.length) { // операция массивом c } }

Ответ 2



String arr1 = new String[]{"a"}; String arr2 = new String[]{"a", "b"}; String arr3 = new String[]{"a", "b", "c"}; for(int i = 0; i < arr1.length + arr2.length + arr3.length; i++) { String arr; int indexOfArr; if(i < arr1.length) { arr = arr1; indexOfArr = i; } else if (i < arr1.length + arr2.length) { arr = arr2; indexOfArr = i - arr1.length; } else { arr = arr3; indexOfArr = i - arr1.length - arr2.length; } System.out.println(arr[indexOfArr]); }

mysql поиск дубликатов

#mysql


Подскажите, как в MySQL можно организовать поиск дубликатов в поле?
Вывести только неповторяющиеся значения можно с помощью DISTINCT, а вот как можно
вывести только повторяющиеся значения?
    


Ответы

Ответ 1



При помощи конструкции HAVING() SELECT fld, COUNT(*) FROM mytable GROUP BY fld HAVING(*) > 1

Перенести ТОЛЬКО измнения одного комита,из одной ветки в другу

#git #git_cherry_pick


Можно ли перенести изменения из одной ветки в другую, без конфликта. 

Хочу сделать такое, у меня есть 2 платы (2 ветки), на которых могу отлаживать алгоритмы,
отличаются платы только файлом инициализации (или комитом инициализации, может быть
несколько файлов)

После добавления изменений в одной ветки, хочу перекинуть только внесенные изменения
в другую ветку. Команда  cherry-pick, делает такое с конфликтом, так как есть изменения
в файлах инициализации.  



Хорошо, опишу чуть подробнее,я занимаюсь embedded программированием и использую git
чисто для себя, что бы
создавать тестовые ветки всякой разной периферии, и не терять(портить) основной проект.  

Так вот по поводу плат вы правильно поняли это печатные плат, с подключением всякой
периферии к процессору
и вот как раз подключение чуть чуть отличается,но есть алгоритмы которые совсем не
зависят от железа вот их я и хочу гонять, то на одной плате то на другой плате,
и хочу что бы все изменения которые внес, на одной плате можно было не включая голову
перенести на другую, и там продолжить.

Моя программа имеет модульный (.с, .h файлы) состав, инициализация каждой периферии,
находится в своем модуле, что бы можно было
кинуть два файла в другой(новый), проект и не искать как же его потом включить. (Но
тут уже да рассматриваю вариант, вынести все функции инициализации в отдельный файл).

Но в данном варианте, такое не подходит по тому что, помимо файлов инициализации,
под каждую плату пришлось вносить некоторые мелкие изменения в несколько модулей, (из
за отличия размера flash в процессора, изменился размер страницы, и на одно плате в
функции передачи данных изменил способ передачи без использования DMA)
хоть это как то и можно вынести, в файл конфигурации, но всё равно что нибудь новое
может вылезти ещё, типа как с изменением способа передачи.

Комментарий player one, навел меня на мысль, сделать для каждого модуля свою репозиторию,
и в новом проекте их все объединять с помощью submdule, не знаю к чему это приведет,
ещё сильно не копал в этом направлении.

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


Ответы

Ответ 1



Я полностью согласен с комментарием player one: вы задаёте какой-то технический вопрос, но возникает он из-за неправильной организации рабочего процесса. И правильно будет рекомендовать вам навести порядок в рабочем процессе, а не пояснять, как разрубить гордиев узел с конкретным техническим вопросом. Судя по вашему описанию, у вас в проекте две ветки, практически полностью идентичные. Вы дублируете очень большое число файлов, помимо одного файла конфигурации и возможно нескольких ещё файлов. И вы постоянно переключаетесь с ветки на ветку, "прыгаете". Вам не кажется, что у вас по сути ДВА проекта, а не ОДИН? И вам нужно два репозитория, один проект = один репозиторий? Тут же всплывает проблема дублирования: это будут два настолько одинаковых репозитория, что настоятельно нужно вспомнить принцип DRY (Don't repeat yourself) и рекомендовать подумать над тем, чтобы убрать ненужные дубликаты. Не зная деталей проекта сложно что-то конкретное рекомендовать. Я бы предложил вынести общую часть в 'core' ("движок") и подключать его к своим двум проектам (получается три репозитория: корневой, с общей частью и два мелких репозитория с отличающимися частями). Не знаю, насколько это подходит к вашему проекту, у меня вообще слово "плата" вызывает ассоциацию с печатными платами, не представляю, как их хранят в гит. А вот насчёт конфигурационного файла тут тоже весьма настоятельно рекомендую задуматься о том, чтобы вынести эти файлы за рамки версионного контроля через механизм .gitignore. Так по крайней мере принято именно в качестве удобного процесса совместной работы нескольких людей (каждый может сделать свои настройки и в гит они не попадут, не будут конфликтовать друг с другом), на разных окружениях (development - stage - production).

Обработка событий

#javascript #html


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

Хотелось бы узнать как производительнее и легче браузеру? Или хотя бы как можно это
правильно протестировать? 

Давайте считать, что у нас на странице много (100-500) обработчиков и сама страница
это лес из div'ов. Мне кажется, за счет того, что между самим событием и его всплытием
до window происходит некоторое время + время расходуется на выбор правильного делегата.
Это может влиять на производительность. Помогите разобраться или это я бешусь с жиру
и всё равно как делать на самом деле.
    


Ответы

Ответ 1



Браузеру производительней обрабатывать события на месте. Имхо не так много причин за вешать обработчик на window, зато куча против. Событию нужно всплыть до window, его могут перехватить и отменить Обработчики будут возбуждаться на все события данного типа и проверять не возникло ли оно на нужном элементе и нужно ли его обработать Обработка будет откладываться пока событие всплывает и обрабатывается более конкретными обработчиками Нельзя будет выстроить порядок обработки повесив дополнительные обработчики на родительский элемент чтобы они выполнились после

Ответ 2



Первое к чему должен стремится каждый человек, это позаботится чтобы его дело приносило только удовольствие. Если Вы будите писать обработчики на window, то Вам придется вести собственный реестр чтобы событие дошло таки до получателя и скорее всего элементам придется давать уникальные id. И мне кажется что если Вы не пишите какой-то фраймворк, то данная затея будет неоправданна. Ведь согласитесь, в реальности не бывает таких вложенностей чтобы заметить разницу производительности на пару кликах. И если тест проведенной под запредельно высокой нагрузкой, которую в жизни не может существовать, покажет существенную разницу, то это может натолкнуть на негативные мысли склоняющие к оптимизации событий. А в реальности события не являются предметом оптимизации, если это не mousemove, scroll или что-то связанное с requestAnimationFrame. Да и то оптимизация сводится к созданию единой точки распространения (создание сервиса, менеджера) и создана лишь для того чтобы минимизировать обращение к dom элементам, работа с которыми является наиболее узким местом при оптимизации приложения.

RecyclerView - динамичное добавление header

#android #recyclerview #header


Мне приходит JSON массив с сервера с датой и количеством сообщений. Мне нужно сделать
список в котором дата будет находиться перед началом сообщений этой даты. Никак не
пойму как можно научить адаптер строить такой список. 

Помогите, покажите пальцем, куда копать? 

Есть примерный способ реализации:


Завести массив с датами сообщений в формате HH:mm
???

    


Ответы

Ответ 1



Организация данных такая, что нужно распарсить JSON в объекты модели, каждая модель будет содержать поле даты и поле записи. Коллекцию необходимо отсортировать по возрастанию даты. Для начала нам понадобится два типа разметок. Первая будет отображать дату и запись (layout.header), вторая - только запись (layout.record). Разметка layout.header включает в себя разметку layout.record через include. layout.record: layout.header: Логика определения, какой тип View выводить основана на сравнении даты текущей записи и прошлой - если они не равны, то выводить заголовок с датой и записью, иначе только запись. Нулевой элемент обрабатывается отдельно и всегда содержит дату и запись. Так же прошу обратить внимание на организацию блока switch - case в методе onBindViewHolder(). При выводе View с датой case выводит дату на разметку и "проваливается" на следующий case (нет оператора break), где на разметку выводится запись. Если заголовок не требуется, то на разметку дата не выводится (срабатывает второй case) class SomeAdapter extends RecyclerView.Adapter { private ArrayList mData; private final int TYPE_HEADER = 0; private final int TYPE_ITEM = 1; public SomeAdapter (ArrayList data) { mData = data; } @Override public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v; switch (viewType) { case TYPE_HEADER: v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false); break; default: v = LayoutInflater.from(parent.getContext()).inflate(R.layout.record, parent, false); } return new ItemHolder(v); } @Override public void onBindViewHolder( ItemHolder holder, int position) { int type = getItemViewType(position); switch (type) { case TYPE_HEADER: holder.mHeaderDate.setText(mData.get(position).getDate())); case TYPE_ITEM: holder.mItemRecord.setText(mData.get(position).getRecord()); break; } } @Override public int getItemCount() { return mData.size(); } @Override public int getItemViewType(int position) { if (isIdentType(position)) return TYPE_ITEM; return TYPE_HEADER; } private boolean isIdentType (int position ){ if (!(position == 0)&&(mData.get(position).getDate()).equals(mData.get(position-1).getDate())) return true; return false; } public static class ItemHolder extends RecyclerView.ViewHolder{ TextView mHeaderDate; TextView mItemRecord; public ItemHolder(View v) { super(v); mHeaderDate = (TextView) v.findViewById(R.id.date); mItemRecord = (TextView) v.findViewById(R.id.record); } } Естественно, разметки нужно оформить покрасивее как то, выделить дату можно другим фоном или другое. Данный пример - только идея.

Первичный ключ в таблице

#sql #sql_server


Повышает ли наличие первичного ключа производительность работы с таблицей, если он
не участвует в условии отбора?

Если не ошибаюсь, то наличие первичного ключа задает способ хранения строк.

Если первичного ключа нет, то все хранится в куче. Так же есть некоторые особенности
в использовании некластиризированных индексов на куче и без кучи.
    


Ответы

Ответ 1



Сам по себе первичный ключ служит для однозначной идентификации строк в таблице, а также для ссылок на строки данной таблицы из других таблиц. Другое дело, что в SqlServer первичный ключ по умолчанию является кластерным индексом. То есть в определении таблицы create table TableName (ID int primary key, ... ); primary key эквивалентно primary key clustered. При желании, однако, вполне можно сделать первичный ключ и некластерным индексом, указав primary key nonclustered. Если не ошибаюсь, то наличие первичного ключа задает способ хранения строк. Если первичного ключа нет, то все хранится в куче. Правильнее будет сформулировать - наличие или отсутствие кластерного индекса (не обязательно, чтобы он был первичным ключом). Если на таблице нет кластерного индекса, то данные таблицы хранятся в куче без какого либо определённого порядка. Если же на таблице есть кластерный индекс, то в нём и содержатся данные таблицы. Кластерный индекс представляет собой "B+"-дерево, в страницах которого лежат ключи в логически упорядоченном виде. В дополнение к ключам в страницах верхних уровней лежат ссылки на страницы следующего уровня, а в страницах нижнего уровня ("листьях") - данные неключевых столбцов. Повышает ли наличие первичного ключа производительность работы с таблицей, если он не участвует в условии отбора? Может повышать. Сканирование кластерного первичного ключа (вообще кластерного индекса) может быть более производительным по сравнению со сканированием кучи, т.к. в кластерном индексе, в отличие от кучи, при изменении данных не создаются переадресованные записи (т.н. forwarded records). Переадресованная запись - это такая запись, которая, увеличившись в размере при обновлении, уже не может помещаться на той странице данных, где она располагалась. В этом случае запись перемещается на другую страницу данных, а на её месте остаётся указатель. Из-за таких указателей увеличивается число чтений, при доступе к данным. Пример. Создадим две почти одинаковых таблицы. Одну с некластерным первичным ключом: create table Heap ( ID int, Name varchar(50), Comments varchar(1000), constraint PK_Heap primary key nonclustered (ID) ); Другую, с такими же столбцами, но с кластерным первичным ключом: create table Cluster ( ID int, Name varchar(50), Comments varchar(1000), constraint PK_Cluster primary key clustered (ID) ); Добавим в таблицы данных: ;with nums as ( select N = row_number() over (order by @@spid) from sys.all_columns a cross join sys.all_columns b ) insert into Cluster (ID, Name, Comments) select top (10000) N, 'Name ' + cast((N - 1) % 1000 + 1 as varchar(10)), 'Comments' from nums order by N; insert into Heap (ID, Name, Comments) select ID, Name, Comments from Cluster; Включив statistics io, выполним одинаковый запрос к каждой из таблиц - такой, чтобы он вызывал их полное сканирование: set statistics io on; declare @cnt int; select @cnt = count(1) from Heap where Name = 'Name 15'; select @cnt = count(1) from Cluster where Name = 'Name 15'; set statistics io off; Статистика показывает, что число чтений пока примерно одинаково: Table 'Heap'. Scan count 1, logical reads 61 ... Table 'Cluster'. Scan count 1, logical reads 63 ... Изменим данные в таблицах (что вызовет появление переадресованных записей в таблице-куче): update Heap set Comments = replicate('More ', 50) + Comments update Cluster set Comments = replicate('More ', 50) + Comments И повторим запросы с count и статистикой к таблицам: Table 'Heap'. Scan count 1, logical reads 9139 ... Table 'Cluster'. Scan count 1, logical reads 680 ... Как видим, хоть данные одни и те же, и изменялись одинаково, и запросы - одинаковые, однако, число чтений, необходимое для сканировании кучи из-за появившихся переадресованных записей выросло на порядок по сравнению с числом чтений, необходимым для сканирования кластерного индекса.

Двухцветный Button [дубликат]

#android #button #background


        
             
                
                    
                        
                            На этот вопрос уже даны ответы здесь:
                            
                        
                    
                
                        
                            Как залить фон button частично?
                                
                                    (2 ответа)
                                
                        
                                Закрыт 3 года назад.
            
                    
Проблема такова. Нужно получить background у button как на этой картинке:


При этом нужно оставить возможность регулировать степень заполнения черным цветом
из кода. 

Как лучше это сделать?
    


Ответы

Ответ 1



Фон кнопки устанавливаете белый, а вместо картинки используете черный цвет, потом с помощью paddingRigth регулируете отступ с права. Примерно вот так: mImageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int width = mImageButton.getWidth(); int procent = 50; mImageButton.setPadding(0, 0, width*procent/100, 0); } }); Разметка:

изменение кодировки oracle sql

#sql #oracle


Имеется бд oracle , в которой nls_lang установлено как russian_russia. Необходимо
поменять кодировку на american.

как я проверяю кодировку : select * from nls_database_parameters.

Пробовал изменить реестр и установить кодировку, что мне нужна. Пробовал прописать
set nls_lang = american_america, но ничего не работает. Переустанавливать не хочется. 

Может есть работающие алгоритмы смены кодировки?
    


Ответы

Ответ 1



Если вы хотите установить окружение, то - set NLS_LANG=american_america. Так Вы измените окружение только для клиентских програм. Я так понял Вам надо изменить на уровне ДБ. Тогда надо в файле параметрах init.ora прописать NLS_LANGUAGE и NLS_TERRITORY отдельно. NLS_LANG не используется в init.ora. UPD Выше сказаное касалось языка и территории. Кодировкa устанавливается в NLS_CHARACTERSET, т.е. v 3-й составляющей NLS_LANG (languge_territory.characterset). Кодировку можно изменить только, если новая кодировка является строгим супермножеством старой кодировки, т.е. нельзя поменять WE8ISO8859P5 на AL32UTF8, а US7ASCII можно всегда. Если повезло, то alter database character set AL32UTF8;. Если нет - миграция данных и в худшем случае програм, которые не совсем понимают новую кодировку. Подробнее читаем тут или в общих чертах по-русски тут.

Изменить ширину NavigationDrawer

#java #android #android_navigation_drawer


Есть activity , в котором я использую кастомный FullDrawerLayout с Gravity=start
, внутри этого activity есть ViewPager внутри которого фрагменты, и вот в одном из
этих фрагментов я хочу добавить NavigationDrawer с Gravity=end и использую уже android.support.v4.widget.DrawerLayout
и вот в нем я хочу изменить ширину, делаю это так:

    drawerRecycler = (RecyclerView) rootView.findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
    drawerRecycler.setLayoutManager(mLayoutManager);
    drawerRecycler.setItemAnimator(new DefaultItemAnimator());

    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) drawerLayout.getLayoutParams();
    params.width = 100;
    drawerLayout.setLayoutParams(params);


но падает с вот такой ошибкой:


  java.lang.IllegalArgumentException: DrawerLayout must be measured with
  MeasureSpec.EXACTLY.


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

public class FullDrawerLayout extends DrawerLayout {

    private static final int MIN_DRAWER_MARGIN = 0; // dp

    public FullDrawerLayout(Context context) {
        super(context);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalArgumentException(
                    "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
        }

        setMeasuredDimension(widthSize, heightSize);

        // Gravity value for each drawer we've seen. Only one of each permitted.
        int foundDrawers = 0;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            if (child.getVisibility() == GONE) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (isContentView(child)) {
                // Content views get measured at exactly the layout's size.
                final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
                final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
                        heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
                child.measure(contentWidthSpec, contentHeightSpec);
            } else if (isDrawerView(child)) {
                final int childGravity =
                        getDrawerViewGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
                if ((foundDrawers & childGravity) != 0) {
                    throw new IllegalStateException("Child drawer has absolute gravity " +
                            gravityToString(childGravity) + " but this already has a " +
                            "drawer view along that edge");
                }
                final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                        MIN_DRAWER_MARGIN + lp.leftMargin + lp.rightMargin,
                        lp.width);
                final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
                child.measure(drawerWidthSpec, drawerHeightSpec);
            } else {
                throw new IllegalStateException("Child " + child + " at index " + i +
                        " does not have a valid layout_gravity - must be Gravity.LEFT, " +
                        "Gravity.RIGHT or Gravity.NO_GRAVITY");
            }
        }
    }

    boolean isContentView(View child) {
        return ((DrawerLayout.LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    boolean isDrawerView(View child) {
        final int gravity = ((DrawerLayout.LayoutParams) child.getLayoutParams()).gravity;
        final int absGravity = Gravity.getAbsoluteGravity(gravity,
                child.getLayoutDirection());
        return (absGravity & (Gravity.LEFT | Gravity.RIGHT)) != 0;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    int getDrawerViewGravity(View drawerView) {
        final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
        return Gravity.getAbsoluteGravity(gravity, drawerView.getLayoutDirection());
    }

    static String gravityToString(int gravity) {
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            return "LEFT";
        }
        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            return "RIGHT";
        }
        return Integer.toHexString(gravity);
    }

}


Здесь падает в строчке 

сhild.measure(contentWidthSpec, contentHeightSpec);

    


Ответы

Ответ 1



Решил проблему, сделал это через верстку, просто установив ширину для RecyclerView и установил ему привязку к правому краю родителя:

ListView и Cursor Adapter android

#android #listview #cursoradaper


Здравствуйте.
Заполняю ListView из адаптера.
По условию, меняю Layout эл-та списка.
При скроле списка, эл-ты перемешиваются, не могу понять, как это победить.

Код адаптера:

public class OrderAdapter extends CursorAdapter {
    private LayoutInflater inflater;

    private class ViewHolder {
        TextView name;
    }

    public OrderAdapter(Context context, Cursor c, int flags) {
        super(context, c, flags);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        ViewHolder holder = (ViewHolder) view.getTag();
        holder.name.setText(cursor.getString(cursor.getColumnIndex("mitm_Name")));
        //holder.time.setText(cursor.getString(holder.timeIndex));
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup
            parent) {

        ViewHolder holder = new ViewHolder();
        View v = null;

        if (cursor.getInt(cursor.getColumnIndex("State")) == 0) {

            v = inflater.inflate(R.layout.cellordername, parent, false);
            holder.name    =   (TextView)  v.findViewById(R.id.txtmitmname);
        }
        else
        {
            v = inflater.inflate(R.layout.cellorder, parent, false);
            holder.name    =   (TextView)  v.findViewById(R.id.txtmitmname);
        }
        v.setTag(holder);
        return v;

    }

}


Переписал адаптер:

public Cursor getOrderData() {

    SQLiteDatabase database = dbHelper.getWritableDatabase();
    String buildSQL = "SELECT * FROM elf_Orders ORDER BY _id";
    return database.rawQuery(buildSQL, null);
}

public class OrderAdapter extends CursorAdapter {
    private LayoutInflater inflater;

    private class ViewHolder {
        TextView name;
    }

    public OrderAdapter(Context context, Cursor c, int flags) {
        super(context, c, flags);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup
            parent) {

        ViewHolder holder = new ViewHolder();

        if (cursor.getInt(cursor.getColumnIndex("State")) == 0) {
            View v = inflater.inflate(R.layout.cellordername, parent, false);
            holder.name = (TextView) v.findViewById(R.id.txtmitmname);
            v.setTag(holder);
            holder.name.setText(cursor.getString(cursor.getColumnIndex("mitm_Name")));
            return v;
        }
        else {
            View v = inflater.inflate(R.layout.cellorder, parent, false);
            holder.name = (TextView) v.findViewById(R.id.txtmitmname);
            v.setTag(holder);
            holder.name.setText(cursor.getString(cursor.getColumnIndex("mitm_Name")));
            return v;
        }
    }

}


Теперь разметки назначаются правильно, но вот данные выводятся в хаотичном порядке
при скроле. В каком направлении копать?
    


Ответы

Ответ 1



Рабочий код адаптера: public Cursor getOrderData() { SQLiteDatabase database = dbHelper.getWritableDatabase(); String buildSQL = "SELECT * FROM elf_Orders ORDER BY _id"; return database.rawQuery(buildSQL, null); } public class OrderAdapter extends CursorAdapter { private LayoutInflater inflater; private class ViewHolder { TextView name; } public OrderAdapter(Context context, Cursor c, int flags) { super(context, c, flags); this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } private int getItemViewType(Cursor cursor) { String type = cursor.getString(cursor.getColumnIndex("State")); if (type.equals("1")) { return 0; } else { return 1; } } @Override public int getItemViewType(int position) { Cursor cursor = (Cursor) getItem(position); return getItemViewType(cursor); } @Override public int getViewTypeCount() { return 2; } @Override public void bindView(View view, Context context, Cursor cursor) { ViewHolder holder = (ViewHolder) view.getTag(); holder.name.setText(cursor.getString(cursor.getColumnIndex("mitm_Name"))); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { ViewHolder holder = new ViewHolder(); View v = null; if (cursor.getInt(cursor.getColumnIndex("State")) == 0) { v = inflater.inflate(R.layout.cellordername, parent, false); holder.name = (TextView) v.findViewById(R.id.txtmitmname); } else { v = inflater.inflate(R.layout.cellorder, parent, false); holder.name = (TextView) v.findViewById(R.id.txtmitmname); } v.setTag(holder); return v; } } Большое спасибо!

Ответ 2



Так, на будущее. Если в Курсор адаптере заполняете if, то обязательно нужно заполнить else, так как listview рандомно меняет сделанные изменения. Я тоже мучался, менял по id элемента цвет textView через if, прокручивал и при возврате был уже хаос. Потом мне подсказали заполнить else тем цветом, что должен быть по стандарту - помогло. Думаю, мой ответ кому-нибудь да поможет. Код класса Adapter, меняющего цвет по id элемента: package ru.diit.healthychildrenfree.adapters; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.TextView; import ru.diit.healthychildrenfree.R; /** * Created by zaynulabid on 12.01.16. */ public class GroupAdapter extends CursorAdapter { private LayoutInflater mInflater; public GroupAdapter(Context context, Cursor cursor){ super(context, cursor, 1); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return mInflater.inflate(R.layout.item, parent,false); } @Override public void bindView(View view, Context context, Cursor cursor) { TextView txName = (TextView) view.findViewById(R.id.textViewNameOfGrup); // инициализировали textView String name = cursor.getString(cursor.getColumnIndex("name_gruppi"));// достали строку в переменную txName.setText(name);// присвоили значение TextView tvOpisanieGruppi = (TextView) view.findViewById(R.id.textViewOpisanieGrup); String opisanieGruppi = cursor.getString(cursor.getColumnIndex("opisanie_gruppi")); tvOpisanieGruppi.setText(opisanieGruppi); long idGroup = cursor.getLong(cursor.getColumnIndex("id_gruppi")); //нашли айди элемента с БД //Log.e("GAdapter", "bindView: idGroup = " + idGroup); if (idGroup == 11){ // если id = 11, меняем цвета текстов на те, что есть в файле colors //Log.e("GAdapter", "bindView: idGroup == 11\nзакрашиваем строчки"); txName.setTextColor(context.getResources().getColor(R.color.textColorTitleFilled)); tvOpisanieGruppi.setTextColor(context.getResources().getColor(R.color.textColorTitleFilled)); } else { // если не равен 11 (как раз этот блок не даст цветам меняться при прокрутке) txName.setTextColor(context.getResources().getColor(R.color.textColorTitle)); tvOpisanieGruppi.setTextColor(context.getResources().getColor(R.color.textColorTitle)); } } } Нет, я не считаю, что это решение правильное, но оно рабочее и при разработке простых задач в курсор-адаптере это решение легко использовать

Как реализовать блок, чтобы при прокрутке залипал у нижней границы окна браузера?

#javascript #jquery


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


Ответы

Ответ 1



Например так: $(window).scroll(function(){ if ($(this).scrollTop() > 100) { $('.arr').fadeIn(); } else{ $('.arr').fadeOut(); } }); $(document).scroll(function() { if ($(window).scrollTop() == $(document).height() - $(window).height()) { $('.arr').addClass('fix'); } else { $('.arr').removeClass('fix'); } }); .footer{ position: relative; } /* Стрелка изначальная */ .arr { display: none; position: fixed; bottom: 0; left: 0; right: 0; margin: 0 auto; } /* Стрелка при скролле до низа страницы */ .arr.fix { position: absolute; top: -50px; bottom: auto; } /* Стили для наглядности */ .arr { width: 0; height: 0; border-left: 25px solid transparent; border-right: 25px solid transparent; border-bottom: 50px solid #000; transition: all .27s ease-in-out; } .content { height: 1500px; border: 1px solid #ccc; } .footer { background: #333; text-align: center; padding: 1rem; color: #fff; }
Некоторый основной контент
Тут подвал


Ответ 2



Альтернативный вариант render(); $(document).scroll(render); function render() { var corner = $('#corner'); var black = $('#black'); var cornerHeight = corner.outerHeight(); corner.each(function(index, el) { if (black.offset().top > ($(window).innerHeight() + $(window).scrollTop())) { $(this).stop().animate({ top: $(window).innerHeight() - cornerHeight + $(window).scrollTop() }, 0); } else { $(this).stop().animate({ top: black.offset().top - cornerHeight }, 0); } }); } html. body { position: relative; } #body { height: 700px; } #black { height: 100px; background-color: black; } #corner { width: 40px; height: 40px; position: absolute; bottom: 0; left: 50%; background-size: cover; background-image: url(https://d30y9cdsu7xlg0.cloudfront.net/png/12990-200.png); } #result { position: fixed; top: 10px; right: 10px; }


Многопоточность и параллельность при запуске внешнего EXE несколько раз

#c_sharp #многопоточность #инспекция_кода #распараллеливание


Добрый день, уважаемое сообщество. 

Прошу провести code review и дать совет. Есть программа.
Ей надо запустить один и тот же внешний EXE файл N раз с разными входными параметрами.
Дождаться окончания выполнения всех запущенных процессов и собрать результаты в единый,
допустим, массив.

Как использовать параллелизм  и асинхронность (если последняя тут нужна) с максимальной
эффективностью? 

Я пока сделала такое решение:

List> processignDataList = ... //список кортежей
с входными данными
Task[] stackTasks = new Task[processignDataList.Count]; // массив всех тасков
List> tasksResults = new List>(); //список
кортежей с результатами отработки тасков
int i = 0; // счетчик, нужен только для указания элемента массива

foreach (Tuple pair in processignDataList) {
  stackTasks[i] = Task.Factory.StartNew(() => tasksResults.Add(Tuple.Create(pair.Item1,
pairProcessing(cSettings, pair))));
  i++; 
}
Task.WaitAll(stackTasks);


Метод string pairProcessing():


берет свойства из специального объекта cSettings, входные параметры
из pair, 
запускает EXE как Process - process.Start(); 
там же находится ограничение от бесконечного зависания - process.WaitForExit(3000)
потом проверка, что process.Close();
анализ StandardOutput, и на его основе 
формирование некой строки и ее возврат как результата работы метода pairProcessing


Достигаю ли я с таким решением запуска нескольких экземпляров EXE и параллельного
их выполнения? Нужны ли какие-то дополнительные пометки методу pairProcessing(), есть
к нему какие-то требования или он может быть любым? Знаете ли вы решения лучше? (а
решение лучше есть всегда...) Есть ли косяки? 
Спасибо.
    


Ответы

Ответ 1



Во-первых, у вас нет синхронизации доступа к tasksResults - тут может быть неприятная гонка. Во-вторых, если процессов будет много - у вас могут закончиться потоки в пуле, что нежелательно ограничит параллелизм. Впрочем, слишком много процессов все равно не смогут работать параллельно. Первая проблема решается довольно просто - вместо записи в список внутри задачи надо позволить задаче вернуть значение: List>> stackTasks = new List>>(); foreach (Tuple pair in processignDataList) { stackTasks.Add(Task.Factory.StartNew(() => Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))); } Tuple[] tasksResults = Task.WhenAll(stackTasks).Result; Также можно воспользоваться linq: Tuple[] tasksResults = Task.WhenAll( from pair in processignDataList select Tuple.Create(pair.Item1, pairProcessing(cSettings, pair))) ).Result; или так: Tuple[] tasksResults = Task.WhenAll( processignDataList.Select(pair => Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))) ).Result; Для решения второй проблемы надо переходить от задач к потокам. Или увеличить размер пула потоков при помощи ThreadPool.SetMaxThreads и ThreadPool.SetMinThreads

Как сохранить пропорции 2D объекта в окне рендера OpenGL?

#cpp #opengl


Есть круг.  



Но если изменить размеры окна(ресайз) например вот так:



или так:  



То получается овал.

Вопрос: как сделать, чтобы при ресайзе, круг всегда оставался кругом?

Код:

#include "stdafx.h"
#include 
#include 

/* подключаем библиотеку GLUT */
#include 

static GLfloat spin = 0.0;


void init(void)
{   
    //glClearColor() устанавливает черный цвет фона
    glClearColor(0.0, 0.0, 0.0, 0.0);
    //glShadeModel(GL_FLAT);////Режим без сглаживания
    glShadeModel(GL_SMOOTH); //Сглаживание. По умолчанию установлен режим GL_SMOOTH.

}

void display(void)
{
    // glClear() очищает фон .
    //В дальнейшем, всякий раз, когда  glClear() будет вызываться,
    //она будет очищать окно в черный цвет .
    glClear(GL_COLOR_BUFFER_BIT);
    glPushMatrix();
    //glRotatef(Angle,Xtrue,Ytrue,Ztrue) отвечает за вращения объекта вдоль
    glRotatef(spin, 0.0, 0.0, 1.0);

    //glColor3f() устанавливает цвет прорисовки - белый цвет.
    glColor3f(1.0, 1.0, 1.0);
    //glColor3f(1.0, 0.0, 0.0);//-красный.

    #define PI 3.1415926535898
    GLint circle_points = 25;
    //glBegin() и glEnd() определяют обьект, который будет прорисован .
    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < circle_points; i++) {
        double angle = 2 * PI*i / circle_points;
        //glVertex2f() определяет вершины полигона, в качестве параметров - 2 координаты
x, y.
        glVertex2f(cos(angle), sin(angle));
    }
    glEnd();


    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < circle_points; i++) {
        double angle = 6 * PI*i / circle_points;
        glVertex2f(cos(angle), sin(angle));
    }
    glEnd();

    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < circle_points; i++) {
        double angle = 20 * PI*i / circle_points;
        glVertex2f(cos(angle), sin(angle));
    }
    glEnd();

    glPopMatrix();
    /*glutSwapBuffers(), делающий свопинг буффера .
    Имеется 2 буффера, и пока на экран не выводится полностью один из них,
    второй остается полностью за кадром, и не произойдет наложения одного надругой .*/
    glutSwapBuffers();
}

void spinDisplay(void)
{
    spin = spin + 0.05;
    if (spin > 360.0)
        spin = spin - 360.0;
    glutPostRedisplay();
}

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //glOrtho() определяет координатную систему .
    glOrtho(-2.0, 2.0, -2.0, 2.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void mouse(int button, int state, int x, int y)
{
    switch (button) {
    case GLUT_LEFT_BUTTON:
        if (state == GLUT_DOWN)
            glutIdleFunc(spinDisplay);
        break;
    case GLUT_MIDDLE_BUTTON:
        if (state == GLUT_DOWN)
            glutIdleFunc(NULL);
        break;
    default:
        break;
    }
}

/*
* double buffer display mode.
* Register mouse input callback functions
*/
int main(int argc, char** argv)
{
    // glutInit(int *argc, char **argv) - самая первая команда инициализации
    glutInit(&argc, argv);
    //glutInitDisplayMode(unsigned int mode) - устанавливает цветовую модель - RGBA
или color - index .
    //Например, glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) -
    //устанавливает  двойной буфер, цветовую модель RGB .
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    //glutInitWindowSize(int width, int size) - размер окна
    glutInitWindowSize(250, 250);
    //glutInitWindowPosition(int x, int y) - установливает начало координат окна.
    glutInitWindowPosition(100, 100);
    //int glutCreateWindow() - создает окно
    glutCreateWindow(argv[0]);
    init();
    //glutDisplayFunc() - вызывается всякий раз при перерисовке окна .
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    //glutMainLoop(void) - эта функция вызывается после всех остальных .
    glutMainLoop();
    return 0;
}


UPDATE:

Добавил -

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    double aspect = (double)w / h;
    //glOrtho() определяет координатную систему .
    glOrtho(-2.0 * aspect, 2.0 * aspect, -2.0, 2.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


Итого всё в порядке,но...

Так хорошо.



Так не хорошо.


    


Ответы

Ответ 1



Вам нужно сделать изменение ширины/высоты в glOrtho при изменении размеров окна, чтобы сохранялись пропорции отрисовки. Попробуйте так: double ww = w, hh = h; if (w > h) glOrtho(-2.0 * (ww/hh), 2.0 * (ww/hh), -2.0, 2.0, -1.0, 1.0); else glOrtho(-2.0, 2.0, -2.0 * (hh/ww), 2.0 * (hh/ww), -1.0, 1.0); В книгах по OpenGL часто такой приём используется.

and и or вместо && и ||

#cpp #keyword


Недавно был изрядно удивлён, увидев в коде вместо привычных && и || ключевые слова
and и or. Возникает вопрос: почему они так редко используются, ведь читаемость, очевидно,
выше, а разницы, судя по документации, никакой?
    


Ответы

Ответ 1



Как говорится, привычка - вторая натура.:) Дело в том, что в C эти токены не являются ключевыми словами. Они являются макросами, которые определены в заголовочном файле . Поэтому для их использования нужно включать этот файл, что добавляет работу программистам.:) Если же вы пишите программы только на C++, то использование этих ключевых слов порой делает код более читабельным. Например, лично я давно взял на вооружение писать not вместо оператора отрицания !, так как последний порой трудно различим в выражениях, содержащих многочисленные скобки. Чтобы не быть голословным, приведу ссылку на мой ответ на SO, где в функции пузырьковой сортировки я использую ключевое слово not. Ключевые слова and и or - также хорошие кандидаты по включению их в свой арсенал при написании логических выражений. Я их тоже порой использую.:)

Как лучше хранить entity?

#javascript #typescript


В общем первый подход был такой 

public entities: Entities.GameEntity[] = [];
public players: Entities.PlayerEntity[] = [];


Когда мне нужно найти entity делаю так 

public findPlayer(playerId: string) {

    let playerFounded: Entities.PlayerEntity = null;
    this.players.forEach((player: Entities.PlayerEntity)=> {
        if (player.client.socketId == playerId) {
            playerFounded = player;
        }
    });

    return playerFounded;
}


Второй подход был такой 

public entities: any = {};
public ids: number[] = [];


Добавление 

entityParams[0] - id of number

this.entities[entityParams[0]] = entity;
this.ids.push(Number(entityParams[0]));


Обход всех 

this.ids.forEach((entityId)=> {
   this.entities[entityId] 
});


ну и если нужно быстро достать то this.entities[entityId]

Как можно более лучше делать такие вещи, и какой из этих подходов лучше
    


Ответы

Ответ 1



Подобную задачу решал создавая специальный класс коллекцию Collection Вспомогательный интерфейс IKey interface IKey { key: string; } Интерфейс коллекции interface ICollection { add(value: T); get(key: string): T; remove(key: string); } И сама реализация. Индекс элемента в массиве храним в хэше, поэтому не нужно делать полный перебор массива. class Collection implements ICollection { private array: T[] = []; private keyToIndex: {[key: string]: number} = {}; add(value: T) { this.array.push(value); this.keyToIndex[value.key] = this.array.length - 1; } get(key: string): T { return this.array[this.keyToIndex[key]]; } remove(key: string) { this.array.splice(this.keyToIndex[key], 1); delete this.keyToIndex[key]; } }

Ответ 2



Если объем данных который будет храниться в массиве небольшой, то может выбирать любой вариант. А вот если данных будет большие, то будут проблемы с производительностью. Я разрабатываю приложения для мобильных устройств (cordova), так вот там у меня возникли эти проблемы (массив был около 1500 элементов). И чтобы решить их мы придумали вот такую хитрость: // recordKeyName {string} первичный ключ // record {any} элемент // records {any[]} массив для обработки setMapping: function (recordKeyName, record, records) { if (!records['maps']) { records['maps'] = {}; records.getRecordByKey = function (link) { return records.maps[link]; }; } records['maps'][record[recordKeyName]] = record; } // индексирование массива for (var i = 0; i <= items.length - 1; i++) { this.setMapping('LINK', items[i], items); } В дополнение к этому мы еще индексируем дополнительные поля, чтобы фильтрация происходила быстрей (в примере этого нет) В итоге получилось так, что при обычном просмотре массива время уходило примерно ~33 мс, а через индексы ~0,08

Можно ли переписать данный метод с использованием EnumerateDirectories?

#c_sharp #net


Не сказал бы, что у меня много папок, которые замедлят работу, но просто интересно.    

static IEnumerable GetFiles(string path)
            {
                Queue queue = new Queue();
                queue.Enqueue(path);
                while (queue.Count > 0)
                {
                    path = queue.Dequeue();
                    try
                    {
                        foreach (string subDir in Directory.GetDirectories(path))//Вот тут
                        {
                            queue.Enqueue(subDir);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.Error.WriteLine(ex);
                    }
                    foreach (string t in Directory.EnumerateFiles(path))
                    {
                        yield return t;
                    }
                }
            }

    


Ответы

Ответ 1



Если вы хотите в одном цикле обработать и папки и файлы, то можно воспользоваться классом DirectoryInfo и переписать цикл так: var dir = new DirectoryInfo(path); foreach(var fso in dir.EnumerateFileSystemInfos()) { if(fso is DirectoryInfo) queue.Enqueue(fso.FullName); if(fso is FileInfo)//перестраховка, по идее должно хватить просто else yield return fso.FullName; } Можно также воспользоваться перегрузкой метода Directory.GetFiles которая дает тот же результат: Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories); Но с одним недостатком - метод упадет с исключением если у вашей программы, например не хватит прав на чтение какой-то папки.

Что это за UI элемент?

#java #android #ui #bottom_sheet


Подскажите, что за UI элемент используется в WhatsApp (а именно, там где Documents,
Camera, ...), это BottomSheet, только его расположили вверху? Или это что-то другое?


    


Ответы

Ответ 1



DialogFragment + RevealEffect. Причем как был классический баг, так они его не исправили. При dispatch touch DialogFragment анимация может стартовать несколько раз, зависит от количества нажатий, пройдя свою duration, обрывается. Смотрится не ок.

Как сделать ссылку неактивной после нажатия?

#html


Есть ссылка, при нажатии на которую нужно вывести блок информации и одновременно
сделать ссылку неактивной (чтобы ее не было видно). Первый пункт я сделал, а сделать
ссылку неактивной не выходит.




Подробнее
   




Подскажите как это сделать?
    


Ответы

Ответ 1



document.getElementById('additional').addEventListener('click', function(e) { e.preventDefault(); e.target.style.display = 'none'; var info = document.getElementById('additional-info'); if (info.style.display === 'none') { info.style.display = 'block'; } else { info.style.display = 'none'; } }, false); Подробнее

Ответ 2



А почему не просто: Some link ? Или: Some link css: .hidden{display: none;} Или вообще вот так: Some description JS: function showSome(obj){ obj.style.display = 'none'; var target = obj.getAttribute('target'); document.getElementById(target).style.display = 'block'; return false; }

В чем разница указать BufferedReader или указать буфер в методе read(byte[] byte)?

#java


Вот есть такой пример 

public static void main(String[] args) throws Exception {
FileInputStream inputStream = new FileInputStream("c:/data.txt");
FileOutputStream outputStream = new FileOutputStream("c:/result.txt");

byte[] buffer = new byte[1000];
while (inputStream.available() > 0){

int count = inputStream.read(buffer); 
outputStream.write(buffer, 0, count); 
}

inputStream.close();
outputStream.close();
}


Вот тут несколько вопросов 


Зачем нужно указывать outputStream.write(buffer, 0, count);
количество байтов для записи (от и до 0, count) , если все равно
метод read(byte[] byte) будет читать байты до тех пор пока буфер
не заполнится или пока байты в файле не кончатся. 

Я так понимаю, что сколько дали байтов столько и записал
outputStream.write(buffer);, зачем ставить от и до?
Зачем указывать буфер когда есть BufferedInputStream()? Если я
правильно понимаю, то он будет работать также как если мы укажем  byte[] byte = new
byte[1000]... Он так же будет буферизировать байты перед записью....


ПРАВКА

По поводу второго моего вопроса вот метод для примера 

private void writeFile(File file, byte[] bytes) {
    BufferedOutputStream bos = null;

    try {
        bos = new BufferedOutputStream(new FileOutputStream(file));
        bos.write(bytes);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (image != null) {
            image.close();
        }
        if (null != bos) {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


ПРАВКА2


    


Ответы

Ответ 1



Методам write(byte[] data) или write(byte[] data, int offset, int length) классов FileOutputStream и BufferedOutputStream для записи массива все равно надо указать массив с прочитанными байтами, так что в любом случае перед записью байтов их надо будет прочитать во внутренний буфер. Так что использование BufferedInputStream вместо FileInputStream не освобождает от необходимости читать данные во внутренний буфер. Более того, если не использовать специфические возможности буферизованных потоков, код для работы с буферизованными и небуферизованными потоками будет вообще одинаковый. Другое дело, что в большинстве случаев работа с буферизованными потоками будет на порядок быстрее. Вы в своих рассуждениях забываете о том, что последний (а возможно, единственный) фрагмент входного файла не обязан по размеру совпадать с размером буфера, так что в любом случае при записи последнего фрагмента необходимо указывать, сколько байтов надо записать - а именно, столько, сколько фактически было прочитано. Иначе при записи к концу файла припишется лишний мусор, которого не было во входном файле (в случае единственного фрагмента это будут нули, а в случае неединственного повторно запишется часть предыдущего фрагмента). Поэтому в любом случае для записи по крайней мере одного (последнего) фрагмента надо будет использовать write(byte[] data, int offset, int length), а использование разных методов для записи последнего и непоследнего фрагментов - это лишнее усложнение кода.

Ответ 2



Создавая массив байт, вы вылеляете 1000 байт в куче, которые java любезно заливает вам нулями. Т.е. в этой строке: byte[] buffer = new byte[1000]; вы говорите: пожалуйста, залей мне 1000 байт нулями и сохрани ссылку на массив в переменную buffer. Когда вы вызываете метод is.read(buffer), он читает то количество байт, которое удалось прочитать. И после возвращает количество байт, которые он прочитал, которое вы и сохраняете в переменную count. После этих манипуляций у вас будет массив из 1000 байт, count из которых будут залиты прочитаными значениями, а остальные байты будут нулями. Чтобы не записывать в файл ненужные нули, которых по сути и не было в исходном файле, вы говорите: os.write(buffer, 0, count), что равносильно: пожалуйста, залей мне в поток байты из буфера buffer начиная с 0-го, всего count штук. А в BufferedOutputStream нет метода write с сигнатурой (byte[]). Еще раз. Так как выходной поток понятия не имеет, хотите вы записать в него весь массив байт, или часть, то он спрашивает у вас начальную позицию и количество байт, которые записывать. Поймите. Это сделано для удобства. Вы можете считывать куда угодно байты из InputStream, создавать правильный массив нужной длины, копировать в него нужные значения и передавать на запись целый массив, не указывая начальную позицию и количество байт, но это: Не рационально. Требуется процессорные время и дополнительный кусок памяти для создания массива нужной длины. Не удобно, так как много лишнего кода. Т.е. подведя черту: с таким api вы можете создать некоторый буфер размером n и переиспользовать его сколько угодно раз для чтения байт из потока, а переменная count будет сообщать, сколько на данный момент содержится валидных байт в вашем буфере. А так же: при чтении из потока в цикле учитывайте то, что может прочитаться меньшее кол-во байт, чем размер вашего буфера. И переменная count будет говорить о том, сколько было прочитано байт. Как-то так

Android: ручная смена языка интерфейса на нестандартный

#android #android_sdk


Как лучше реализовать смену языка в при разработке приложений под Android, если это
какие-нибудь нестандартные языки (например, диалекты китайского или устаревший русский)?
Такие языки в принципе не логично настраивать автоматически, потому меня в этом вопросе
интересуют две вещи:


Как правильно в данном случае (т. е. ручная смена языка без локали) создать res-файл
с дополнительным языком? Наверняка не так же, как обычно.
Как лучше активировать этот язык через приложение? Понятно, что надо будет создать
соответствующий пункт в настройках, но каком принципе следует создать метод, сменяющий
язык интерфейса?

    


Ответы

Ответ 1



Вы можете создавать свои собственные локали. И работать с ними как со стандартными, как в плане ресурсов так и кода. Например создадим локаль "ru_MY". Пример переключения локалей есть на кртинке. но лучше приведу его кодом // set your locale try { Constructor constructor = Locale.class.getConstructor(String.class, String.class); constructor.setAccessible(true); Locale locale = constructor.newInstance("ru", "MY"); // <-- you locale (for example ru_MY) Locale.setDefault(locale); Configuration config = getBaseContext().getResources().getConfiguration(); config.locale = locale; getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } catch (Throwable e) { e.printStackTrace(); } PS не забуде что диалект и страна разграничиваются буквой "r" values-ru-rMY

С чем ассоциировать .desktop файл, чтобы он просто запускался?

#linux #ubuntu #linux_mint


Допустим, у меня по умолчанию в качестве текстого редактора используется Geany. Если
я в терминале наберу xdg-open something.desktop, то файл откроется в Geany, но мне
нужно чтобы он запускался!

На скриншоте вы видите, что в контекстном меню предлагают открыть ярлык офисом, т.е.
связанным приложением и это очень неудобно! Я не хочу открывать его офисом, я хочу
чтоб в этом меню предлагался текстовый редактор, но текстовый редактор уже является
приложением по умолчанию и по этому его там нет.

Мне нужно как-то перенести Geany из разряда "Приложение по умолчанию" в разряд "Связанные
приложения", а приложением по умолчанию назначить команду запуска. Подскажите пожалуйста,
как это сделать? 


    


Ответы

Ответ 1



ответ будет (в последней части) пересекаться с другим моим ответом: Каким образом программно зарегистрировать в ОС программу для открытия определенного типа файлов по умолчанию?, но уже не в общесистемном, а в пользовательском контексте. для начала надо уметь извлечь из desktop-файла имя программы (оно указывается в строке, начинающейся с exec=): с помощью такого скрипта: #!/bin/bash f=$1 shift $(sed -n '/^Exec=/{s/^Exec=//;s/%.//;p}' $f) "$@" & или такого: #!/usr/bin/python from gi.repository import Gio import sys def main(myname, desktop, *uris): launcher = Gio.DesktopAppInfo.new_from_filename(desktop) launcher.launch_uris(uris, None) if __name__ == "__main__": main(*sys.argv) сохраните его под произвольным именем (например, deskopen) в каком-нибудь из каталогов, перечисленных в переменной окружения $PATH (например, в /usr/local/bin) и присвойте биты исполнимости ($ chmod +x /путь/к/файлу). теперь desktop-файлы можно «запускать» с помощью этого скрипта: $ deskopen /путь/к/файлу.с.суффиксом.desktop теперь надо создать desktop-файл, служащий «обёрткой» для вызова этого скрипта: [Desktop Entry] Name=deskopen Exec=deskopen %U MimeType=application/x-desktop Terminal=false Type=Application для использования только текущим пользователем его надо поместить под именем deskopen.desktop в каталог ~/.local/share/applications. в том же каталоге (~/.local/share/applications) эту ассоциацию — между mime-типом application/x-desktop и созданной на предыдущем шаге «обёрткой» надо сделать «умолчальной». в один из файлов — defaults.list или mimeapps.list — добавьте в секцию [Default Applications] строку: application/x-desktop=deskopen.desktop если ни одного из этих двух файлов не существовало — создайте любой из них. тогда он будет выглядеть так: [Default Applications] application/x-desktop=deskopen.desktop всё. теперь команда $ xdg-open /путь/к/файлу.с.суффиксом.desktop будет запускать упомянутую в этом файле программу, а не редактор для редактирования этого файла. соответственно, и «файловые менеджеры» будут вести себя аналогично при «щелчке» по такому файлу.

Плавная анимация назад

#css #html5 #css3 #svg #css_animation




.container {
  padding: 2em 0;
  width: 50%;
  height: 6em;
  margin: 3em auto;
  background: grey;
}
    
.item {
  width: 4em;
  height: 4em;
  background: pink;
  margin: 0 auto;
}
    
.item:hover {
  animation: levitation 3s linear 0.1s infinite alternate;
}
    
@keyframes levitation {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-30%);
  }
}
При hover на .item блок плавно левитирует, но при убирании hover-эффекта от блока, он резко падает. Что нужно дописать в css, чтобы при убирании hover-эффекта блок плавно опускался вниз?


Ответы

Ответ 1



Вариант с JS и подпиской на события анимации: var matchesSelector = (function() { var fn = Element.prototype.matches || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector; return function(element, selector) { return fn.call(element, selector); }; })(); function setAnimatedOnHover(element){ function onMouseOver(){ element.classList.add('animated'); } function onAnimationEnd(){ if (matchesSelector(element, ':hover')) return; element.classList.remove('animated'); } element.addEventListener('mouseover', onMouseOver, false); element.addEventListener('webkitAnimationIteration', onAnimationEnd, false); element.addEventListener('animationiteration', onAnimationEnd, false); } [].forEach.call(document.querySelectorAll('.animated-on-hover'), setAnimatedOnHover); .container { padding: 2em 0; width: 50%; height: 6em; margin: 3em auto; background: grey; } .item { width: 4em; height: 4em; background: pink; margin: 0 auto; } .item.animated { animation: levitation 1s linear 0.1s infinite alternate; } @keyframes levitation { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-30%); } }
Альтернативный вариант на чистом CSS (довольно своеобразный, конечно): .container { padding: 2em 0; width: 50%; height: 6em; margin: 3em auto; background: grey; } .item-zero { height: 0; transition: height 1s; animation: levitation 3s linear 0.1s infinite alternate; } .item-zero:hover { height: 4em; } .item { width: 4em; height: 4em; background: pink; margin: 0 auto; } @keyframes levitation { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-30%); } }


Ответ 2



SVG решение Наведите курсор мышки на квадратики в любом очередности Начало анимации begin="mouseover" Чтобы квадратики не дергались при повторном наведении курсора, установлен запрет на повторный запуск анимации. restart="whenNotActive" Перемещение по оси "Y" во время анимации values="150;50;150"

Как в JavaScript нужно правильно деинициализировать объект?

#javascript


Достаточно ли в JavaScript для того, чтобы деинициализировать объект, установить
this в null? Под деинициализировать я имею в виду полностью удалить его, очистив занимаемую
им память.

Например, можно ли внутри метода совершенно другого класса сделать так:

class MainClass() {
    constructor() {
        this.anotherClassObject = new AnotherClass();
    }
    destroyAnotherClassObject() {
        this.anotherClassObject = null;
    }
}

mainObject = new MainClass();
mainObject.destroyAnotherClassObject();


Достаточно ли будет этого, чтобы удалить объект и, главное, освободить из-под него
память?
    


Ответы

Ответ 1



В javascript объект полностью удалится только в том случаи, если до него будет невозможно добраться из объекта window. То есть пока до объекта можно добраться по ссылкам начиная от самого корневого объекта window, то он будет находится в памяти, Garbage Collection или сокращенно GC его не уничтожит. Другими словами, чтобы объект был удален, нужно удалить на него все ссылки. Конкретно в Вашем случаи достаточно просто "занулить" ссылку на сам экземпляр. class SomeClass { } let someInstance = new SomeClass(); someInstance = null; // этого достаточно чтобы объект был удален из памяти Если рассматривать конкретный пример автора (код ниже), то вышесказанное верно и для него. Ведь ссылка someInstance на экземпляр класса SomeClass объявлена в глобальном контексте и уже по ней можно получить доступ к созданному внутри экземпляру другого класса AnotherClass - window.someInstance.anotherInstance. class SomeClass { constructor(){ this.anotherInstance = new AnotherClass(); } destroy(){ this.anotherInstance = null; } } let someInstance = new SomeClass(); // если по какой-то причине экземпляр someInstance // остается нужным, а необходимости в экземпляре AnotherClass // уже нет, его можно удалить вызвав someInstance.destroy() И напоследок остается напомнить что в js с помощью замыканий можно имитировать приватный свойства и функции, до которых будет невозможно добраться от самого корневого объекта, что вызовет увеличение потребления памяти до тех пор, пока не будет удален объект содержащий в себе это замыкание.

Ответ 2



Объект в JavaScript будет удален когда на него никто не будет ссылаться. Если Ваш объект занимает много памяти (например содержит в себе большой массив данных) и Вам нужно принудительно ее освободить, то напишите какой-нибудь метод Free у класса, который эту память освободит и вызывайте его по необходимости. Но опять таки, эта память освободится, когда на нее никто не будет ссылаться. Так, что целесообразно такие поля делать приватными

Как удалить файл из проекта .csproj не используя Visual Studio?

#c_sharp


Что нужно подправить в файле проекта .csproj, чтобы удалить исходный файл из проекта
С# и чтобы после удаления этого исходного файла, проект успешно запускался.
Достаточно ли удалить все что находится в теге Compile? И что делать с файлами, которые
находятся в теге  DependentUpon?
Я комментировал все что находилось между тегами Compile и если там еще находил тег
DependentUpon, то находил название исходника, заключенного в тег Page и комментировал
все вместе с тегом Page.
Кто сталкивался с такой задачей прошу помочь. 
    


Ответы

Ответ 1



Если Вам нужно удалить файл MainWindowView.xaml.cs Удаляйте из .csproj весь элемент , в Вашем случае это: MainWindowView.xaml Code Ok, файл с кодом MainWindowView.xaml.cs удален из проекта. Остался файл с разметкой MainWindowView.xaml, обычно они идут парами и не имеют смысла один без другого. Тем не менее, если удалить файл с кодом проект собирается(покрайней мере у меня). Нужно ли Вам теперь удалить из проекта MainWindowView.xaml - я не знаю. Ответ зависит от того зачем вы вообще это делаете. Но сдается мне что правильный ответ будет удалить его тоже, таким же методом. Тоесть удалить элемент : MSBuild:Compile Designer

Ответ 2



Почему нельзя удалить через IDE? Но если интересна структура, то вообще достаточно удалить тег в и если в коде этот класс не используется, то все должно собраться(не удаляйте те классы которые используются у вас в проекте.)

Обход массива в глубину: код подсчёта количества грядок на садовом участке не проходит тесты на сайте

#python #массивы #алгоритм #python_3x


Прямоугольный садовый участок шириной N и длиной M метров разбит на квадраты со стороной
1 метр.
На этом участке вскопаны грядки. Грядкой называется совокупность квадратов, удовлетворяющая 
таким условиям:

-из любого квадрата этой грядки можно попасть в любой другой квадрат этой же грядки,

-последовательно переходя по грядке из квадрата в квадрат через их общую сторону;

-никакие две грядки не пересекаются и не касаются друг друга ни по вертикальной, 
ни по горизонтальной сторонам квадратов (касание грядок углами квадратов допускается).

Подсчитайте количество грядок на садовом участке.

Входные данные:

5 10
##......#.
.#..#...#.
.###....#.
..##....#.
........#.


Дополнительные модули не использовать.

В первой строке находятся числа N и M через пробел, далее идут N строк по M символов.
Символ # обозначает территорию грядки, точка соответствует незанятой территории. Других
символов нет. (1 ≤ N, M ≤ 200)



Основная проблема, в том, что код проверяется на сайте, где он проверяется по 12
тестам. Условия тестов не указаны, но одновременно их все пройти не получается...

мой лучший вариант: 11 из 12:

try:
    n, m = input().split()
    n = int(n)
    m = int(m)
    alll = [[] for i in range(n)]
    visited = [[False for j in range(m)] for i in range(n)]
    num = 0

    for i in range(n):
        row = list(input())
        for j in range(m):
            alll[i].append(row[j])

    def dfs(x, y):
        visited [x][y]= True
        for dx, dy in [[-1, 0], [0, -1], [0, 1], [1, 0]]:
            if  0 <= x + dx <= n-1 and 0 <= y + dy <= m-1:
                if alll[x + dx][y + dy] == "#" and not visited[x + dx][y + dy]:
                    dfs(x + dx, y + dy)

    for x in range(n):
        for y in range(m):
            if alll [x][y]== "#" and not visited[x][y]:
                dfs(x, y)
                num += 1
except Exception:
    num += 1

print(num)


есть еще: 10 из 12:

def solve1(N, M, case):
    l = [[0] * M for i in range(N)]
    n = 0
    connected = set()
    for i in range(N):
        for j in range(M):
            x = 0
            if case[i][j] == '#': 
                if (i > 0 and l[i-1][j] != 0):
                    x = l[i-1][j]
                left = l[i][j-1]
                if (j > 0 and left != 0):
                    if x != left:
                        if x > 0:
                            connected.add(frozenset((x, left)))
                        else:
                            x = left
                if x == 0:
                    n += 1
                    x = n
                l[i][j] = x
#print('\n'.join(str(r) for r in case))
#print('\n'.join(str(r) for r in l))
#print(connected)
    return n - len(connected)

N, M = map(int, input().split()) 
case = [ list(input()) for _ in range(N) ]
print(solve1(N, M, case))


Был бы рад конструктивной критике. Спасибо за внимание)
    


Ответы

Ответ 1



Чтобы упростить условия в dfs(), можно окружить участок пустыми квадратами. Иначе алгоритм такой же как в первом примере в вопросе: Проверяем принадлежит ли квадрат ещё непосещённой грядке Посещаем все квадраты, принадлежащие этой грядке Повторяем пока все квадраты в огороде не проверим. Решение в лоб (числа входные маленькие). Не проверял на сайте, можете со своим кодом для разных вводов сравнить: #!/usr/bin/env python3 BED = '#' # mark garden bed WEST, NORTH, EAST, SOUTH = (-1, 0), (0, -1), (1, 0), (0, 1) # directions # read input N, M = map(int, input().split()) garden = [input().strip() for _ in range(N)] # surround the garden with empty squares garden[:] = [' '*(M+2)] + [' ' + row + ' ' for row in garden] + [' '*(M+2)] def visit_garden_bed(i, j, seen): """Enumerate all garden bed's squares""" q = [(i, j)] while q: i, j = vertex = q.pop() seen.add(vertex) for dx, dy in [WEST, NORTH, EAST, SOUTH]: # all possible directions y, x = vertex = (i+dy, j+dx) if garden[y][x] == BED and vertex not in seen: q.append(vertex) return 1 # the number of garden beds visited # visit all garden beds seen = set() # (i, j) print(sum(visit_garden_bed(i, j, seen) for i, row in enumerate(garden) for j, square in enumerate(row) if square == BED and (i, j) not in seen))

Возможность докачки файла

#java #spring


У пользователей слабый интернет, и бывает связь разрывается.
Отправляю файл так

FileInputStream inputStream = new FileInputStream(downloadFile);
        // get MIME type of the file
        String mimeType = context.getMimeType(file.toString());
        if (mimeType == null) {
            // set to binary type if MIME mapping not found
            mimeType = "application/octet-stream";
        }
        // set content attributes for the response
        response.setContentType(mimeType);
        response.setContentLength((int) downloadFile.length());
        // set headers for the response
        String headerKey = "Content-Disposition";
        String headerValue = String.format("attachment; filename*0*=utf-8''\"%s\"",
        URLEncoder.encode(downloadFile.getName(), "UTF-8"));
        response.setHeader(headerKey, headerValue);
        // get output stream of the response
        OutputStream outStream = null;
        try {
            outStream = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        // write bytes read from the input stream into the output stream
        try {
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        inputStream.close();
        outStream.close();
        return null;
    }


Возможно ли добавить реализацию докачки файла если допустим у пользователя оборвалась
связь либо он поставил на паузу? 
    


Ответы

Ответ 1



Используйте jetty, есть готовый проект по вашему случаю -> https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-websocket-jetty А изобретать костыли с использованием WebSocket не комильфо. Или пробуйте стандартными средствами RMI - быстро/проверенно

Ответ 2



Кому интересно, воспользовался этим методом. Всё работает, подкачка поддерживается. https://gist.github.com/davinkevin/b97e39d7ce89198774b4 MultipartFileSender.fromPath(file.getLocalPath()) .with(request) .with(response) .serveResource(); public class MultipartFileSender { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final int DEFAULT_BUFFER_SIZE = 20480; // ..bytes = 20KB. private static final long DEFAULT_EXPIRE_TIME = 604800000L; // ..ms = 1 week. private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES"; Path filepath; HttpServletRequest request; HttpServletResponse response; public MultipartFileSender() { } public static MultipartFileSender fromPath(Path path) { return new MultipartFileSender().setFilepath(path); } public static MultipartFileSender fromFile(File file) { return new MultipartFileSender().setFilepath(file.toPath()); } public static MultipartFileSender fromURIString(String uri) { return new MultipartFileSender().setFilepath(Paths.get(uri)); } //** internal setter **// private MultipartFileSender setFilepath(Path filepath) { this.filepath = filepath; return this; } public MultipartFileSender with(HttpServletRequest httpRequest) { request = httpRequest; return this; } public MultipartFileSender with(HttpServletResponse httpResponse) { response = httpResponse; return this; } public void serveResource() throws Exception { if (response == null || request == null) { return; } if (!Files.exists(filepath)) { logger.error("File doesn't exist at URI : {}", filepath.toAbsolutePath().toString()); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } Long length = Files.size(filepath); String fileName = filepath.getFileName().toString(); FileTime lastModifiedObj = Files.getLastModifiedTime(filepath); if (StringUtils.isEmpty(fileName) || lastModifiedObj == null) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } long lastModified = LocalDateTime.ofInstant(lastModifiedObj.toInstant(), ZoneId.of(ZoneOffset.systemDefault().getId())).toEpochSecond(ZoneOffset.UTC); String contentType = MimeTypeUtils.probeContentType(filepath); // Validate request headers for caching --------------------------------------------------- // If-None-Match header should contain "*" or ETag. If so, then return 304. String ifNoneMatch = request.getHeader("If-None-Match"); if (ifNoneMatch != null && HttpUtils.matches(ifNoneMatch, fileName)) { response.setHeader("ETag", fileName); // Required in 304. response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } // If-Modified-Since header should be greater than LastModified. If so, then return 304. // This header is ignored if any If-None-Match header is specified. long ifModifiedSince = request.getDateHeader("If-Modified-Since"); if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) { response.setHeader("ETag", fileName); // Required in 304. response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } // Validate request headers for resume ---------------------------------------------------- // If-Match header should contain "*" or ETag. If not, then return 412. String ifMatch = request.getHeader("If-Match"); if (ifMatch != null && !HttpUtils.matches(ifMatch, fileName)) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // If-Unmodified-Since header should be greater than LastModified. If not, then return 412. long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since"); if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // Validate and process range ------------------------------------------------------------- // Prepare some variables. The full Range represents the complete file. Range full = new Range(0, length - 1, length); List ranges = new ArrayList<>(); // Validate and process Range and If-Range headers. String range = request.getHeader("Range"); if (range != null) { // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416. if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) { response.setHeader("Content-Range", "bytes */" + length); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } String ifRange = request.getHeader("If-Range"); if (ifRange != null && !ifRange.equals(fileName)) { try { long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid. if (ifRangeTime != -1) { ranges.add(full); } } catch (IllegalArgumentException ignore) { ranges.add(full); } } // If any valid If-Range header, then process each part of byte range. if (ranges.isEmpty()) { for (String part : range.substring(6).split(",")) { // Assuming a file with length of 100, the following examples returns bytes at: // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100). long start = Range.sublong(part, 0, part.indexOf("-")); long end = Range.sublong(part, part.indexOf("-") + 1, part.length()); if (start == -1) { start = length - end; end = length - 1; } else if (end == -1 || end > length - 1) { end = length - 1; } // Check if Range is syntactically valid. If not, then return 416. if (start > end) { response.setHeader("Content-Range", "bytes */" + length); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } // Add range. ranges.add(new Range(start, end, length)); } } } // Prepare and initialize response -------------------------------------------------------- // Get content type by file name and set content disposition. String disposition = "inline"; // If content type is unknown, then set the default value. // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp // To add new content types, add new mime-mapping entry in web.xml. if (contentType == null) { contentType = "application/octet-stream"; } else if (!contentType.startsWith("image")) { // Else, expect for images, determine content disposition. If content type is supported by // the browser, then set to inline, else attachment which will pop a 'save as' dialogue. String accept = request.getHeader("Accept"); disposition = accept != null && HttpUtils.accepts(accept, contentType) ? "inline" : "attachment"; } logger.debug("Content-Type : {}", contentType); // Initialize response. response.reset(); response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setHeader("Content-Type", contentType); response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); logger.debug("Content-Disposition : {}", disposition); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", fileName); response.setDateHeader("Last-Modified", lastModified); response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME); // Send requested file (part(s)) to client ------------------------------------------------ // Prepare streams. try (InputStream input = new BufferedInputStream(Files.newInputStream(filepath)); OutputStream output = response.getOutputStream()) { if (ranges.isEmpty() || ranges.get(0) == full) { // Return full file. logger.info("Return full file"); response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + full.start + "-" + full.end + "/" + full.total); response.setHeader("Content-Length", String.valueOf(full.length)); Range.copy(input, output, length, full.start, full.length); } else if (ranges.size() == 1) { // Return single part of file. Range r = ranges.get(0); logger.info("Return 1 part of file : from ({}) to ({})", r.start, r.end); response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); response.setHeader("Content-Length", String.valueOf(r.length)); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Copy single part range. Range.copy(input, output, length, r.start, r.length); } else { // Return multiple parts of file. response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Cast back to ServletOutputStream to get the easy println methods. ServletOutputStream sos = (ServletOutputStream) output; // Copy multi part range. for (Range r : ranges) { logger.info("Return multi part of file : from ({}) to ({})", r.start, r.end); // Add multipart boundary and header fields for every range. sos.println(); sos.println("--" + MULTIPART_BOUNDARY); sos.println("Content-Type: " + contentType); sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total); // Copy single part range of multi part range. Range.copy(input, output, length, r.start, r.length); } // End with multipart boundary. sos.println(); sos.println("--" + MULTIPART_BOUNDARY + "--"); } } } private static class Range { long start; long end; long length; long total; /** * Construct a byte range. * @param start Start of the byte range. * @param end End of the byte range. * @param total Total length of the byte source. */ public Range(long start, long end, long total) { this.start = start; this.end = end; this.length = end - start + 1; this.total = total; } public static long sublong(String value, int beginIndex, int endIndex) { String substring = value.substring(beginIndex, endIndex); return (substring.length() > 0) ? Long.parseLong(substring) : -1; } private static void copy(InputStream input, OutputStream output, long inputSize, long start, long length) throws IOException { byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int read; if (inputSize == length) { // Write full range. while ((read = input.read(buffer)) > 0) { output.write(buffer, 0, read); output.flush(); } } else { input.skip(start); long toRead = length; while ((read = input.read(buffer)) > 0) { if ((toRead -= read) > 0) { output.write(buffer, 0, read); output.flush(); } else { output.write(buffer, 0, (int) toRead + read); output.flush(); break; } } } } } private static class HttpUtils { /** * Returns true if the given accept header accepts the given value. * @param acceptHeader The accept header. * @param toAccept The value to be accepted. * @return True if the given accept header accepts the given value. */ public static boolean accepts(String acceptHeader, String toAccept) { String[] acceptValues = acceptHeader.split("\\s*(,|;)\\s*"); Arrays.sort(acceptValues); return Arrays.binarySearch(acceptValues, toAccept) > -1 || Arrays.binarySearch(acceptValues, toAccept.replaceAll("/.*$", "/*")) > -1 || Arrays.binarySearch(acceptValues, "*/*") > -1; } /** * Returns true if the given match header matches the given value. * @param matchHeader The match header. * @param toMatch The value to be matched. * @return True if the given match header matches the given value. */ public static boolean matches(String matchHeader, String toMatch) { String[] matchValues = matchHeader.split("\\s*,\\s*"); Arrays.sort(matchValues); return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1; } } }

Как получить индекс строки в WinForms, DevExpress

#c_sharp #winforms #devexpress


Как получить индекс или значения выбранной строки в gridControl от DevExpress в WinForms?
В стандартном datagridview делаю так =  

gridOrder.SelectedRows[i].Cells["order_number"].Value


Хочу заменить стандартный datagridview на gridControl, но встречаю много отличий,
думал они будут идентичный просто gridControl более функционален, а получается они
почти разные? Может есть документация на русском у кого-то для него?
    


Ответы

Ответ 1



Используйте метод ColumnView.GetSelectedRows для получения индексов выделенных строк: int[] rowHandles = gridView1.GetSelectedRows(); Если множественное выделение отключено(по умолчанию или с помощью опции режима выделения ColumnView.OptionsSelection.MultiSelect) используйте свойство ColumnView.FocusedRowHandle: int rowHandle = gridView1.FocusedRowHandle; Получить же сами значения ячеек или объект строки целиком можно с помощью методов ColumnView.GetRowCellValue/ColumnView.GetRow: int number = (int)gridView1.GetRowCellValue(rowHandle, "order_number"); //... Order order = (Order)gridView1.GetRow(rowHandle); Для сфокусированной строки есть "сокращенные" методы GetFocusedRow/GetFocusedRowCellValue p.s. Полезные ссылки: Focus and Selection Handling Obtaining and Setting Cell Values p.p.s. Как верно заметили в комментариях, MVVM можно использовать и в WinForms, и как раз DevExpress является единственным вендором, предоставляющим поддержку MVVM на уровне WinForms контролов из коробки. Для случая с выделенной строкой код биндинга может выглядеть вот так: // привязка вьюмодели списка заказов к гриду var fluent = mvvmContext1.OfType(); // привязка индикатора загрузки(если во вьюмодели есть асинхронка) fluent.SetBinding(gridView1, gView => gView.LoadingPanelVisible, x => x.IsLoading); fluent.SetBinding(gridControl1, gControl => gControl.DataSource, x => x.Entities); // привязка выделенной строки fluent.WithEvent(gridView1, "FocusedRowObjectChanged") .SetBinding(x => x.SelectedEntity, args => args.Row as Order, (gView, entity) => gView.FocusedRowHandle = gView.FindRow(entity));